Skip to content

Commit

Permalink
TT-4354 Go plugins to support multiple Tyk versions (TykTechnologies#…
Browse files Browse the repository at this point in the history
…3873)

* started to build new plugin path based on gwVersion+os+architecture

* added test coverage.Small refactor to load go plugin

* if goarch and goos are present then build the plugin name using those values

* receive params via terminal

* rename the plugin name and not the path

* fix naming of the plugin format

* build the plugin considering os and arch params

* enable cross compiling

* set CGO_ENABLE=1

* enable cgo only when its not linux

Co-authored-by: Tomas Buchaillot <[email protected]>
  • Loading branch information
sredxny and tbuchaillot authored Feb 25, 2022
1 parent 74664bd commit e60c6f2
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 10 deletions.
7 changes: 4 additions & 3 deletions gateway/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import (
"crypto/x509/pkix"
"encoding/json"
"fmt"
"github.com/TykTechnologies/tyk/headers"
"github.com/TykTechnologies/tyk/storage"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net"
"net/http"
Expand All @@ -20,6 +17,10 @@ import (
"testing"
"time"

"github.com/TykTechnologies/tyk/headers"
"github.com/TykTechnologies/tyk/storage"
"github.com/stretchr/testify/assert"

"github.com/TykTechnologies/tyk/user"

"github.com/TykTechnologies/tyk/apidef"
Expand Down
10 changes: 5 additions & 5 deletions gateway/handler_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ const (
defaultTemplateFormat = "json"
defaultContentType = headers.ApplicationJSON

MsgAuthFieldMissing = "Authorization field missing"
MsgApiAccessDisallowed = "Access to this API has been disallowed"
MsgBearerMailformed = "Bearer token malformed"
MsgKeyNotAuthorized = "Key not authorised"
MsgOauthClientRevoked = "Key not authorised. OAuth client access was revoked"
MsgAuthFieldMissing = "Authorization field missing"
MsgApiAccessDisallowed = "Access to this API has been disallowed"
MsgBearerMailformed = "Bearer token malformed"
MsgKeyNotAuthorized = "Key not authorised"
MsgOauthClientRevoked = "Key not authorised. OAuth client access was revoked"
MsgKeyNotAuthorizedUnexpectedSigningMethod = "Key not authorized: Unexpected signing method"
)

Expand Down
29 changes: 29 additions & 0 deletions gateway/mw_go_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"runtime"
"strings"
"time"

"github.com/TykTechnologies/tyk/apidef"
Expand Down Expand Up @@ -122,6 +125,12 @@ func (m *GoPluginMiddleware) loadPlugin() bool {

// try to load plugin
var err error

if !FileExist(m.Path) {
// if the exact name doesn't exist then try to load it using tyk version
m.Path = m.goPluginFromTykVersion()
}

if m.handler, err = goplugin.GetHandler(m.Path, m.SymbolName); err != nil {
m.logger.WithError(err).Error("Could not load Go-plugin")
return false
Expand Down Expand Up @@ -218,3 +227,23 @@ func (m *GoPluginMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Reque

return
}

// goPluginFromTykVersion builds a name of plugin based on tyk version
// os and architecture. The structure of the plugin name looks like:
// {plugin-dir}/{plugin-name}_{GW-version}_{OS}_{arch}.so
func (m *GoPluginMiddleware) goPluginFromTykVersion() string {
if m.Path == "" {
return ""
}

pluginDir := filepath.Dir(m.Path)
// remove plugin extension to have the plugin's clean name
pluginName := strings.TrimSuffix(filepath.Base(m.Path), ".so")
os := runtime.GOOS
architecture := runtime.GOARCH

newPluginName := strings.Join([]string{pluginName, VERSION, os, architecture}, "_")
newPluginPath := pluginDir + "/" + newPluginName + ".so"

return newPluginPath
}
37 changes: 37 additions & 0 deletions gateway/mw_go_plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package gateway

import (
"fmt"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGoPluginFromTykVersion(t *testing.T) {
m := GoPluginMiddleware{
BaseMiddleware: BaseMiddleware{},
Path: "",
SymbolName: "test-symbol",
}

type testCase struct {
userDefinedName, inferredName string
}
os := runtime.GOOS
arch := runtime.GOARCH

matrix := []testCase{
{"plugin.so", fmt.Sprintf("./plugin_%v_%v_%v.so", VERSION, os, arch)},
{"/some/path/plugin.so", fmt.Sprintf("/some/path/plugin_%v_%v_%v.so", VERSION, os, arch)},
{"/some/path/plugin", fmt.Sprintf("/some/path/plugin_%v_%v_%v.so", VERSION, os, arch)},
{"./plugin.so", fmt.Sprintf("./plugin_%v_%v_%v.so", VERSION, os, arch)},
{"", ""},
}

for _, v := range matrix {
m.Path = v.userDefinedName
newPluginPath := m.goPluginFromTykVersion()
assert.Equal(t, v.inferredName, newPluginPath)
}
}
2 changes: 1 addition & 1 deletion gateway/mw_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ func (k *JWTMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, _
switch k.Spec.JWTSigningMethod {
case HMACSign:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf( "%v: %v and not HMAC signature", UnexpectedSigningMethod, token.Header["alg"])
return nil, fmt.Errorf("%v: %v and not HMAC signature", UnexpectedSigningMethod, token.Header["alg"])
}
case RSASign:
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
Expand Down
12 changes: 12 additions & 0 deletions gateway/util.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package gateway

import (
"errors"
"os"
)

// appendIfMissing appends the given new item to the given slice.
func appendIfMissing(slice []string, newSlice ...string) []string {
for _, new := range newSlice {
Expand Down Expand Up @@ -87,3 +92,10 @@ func greaterThanInt(first, second int) bool {

return first > second
}

func FileExist(filepath string) bool {
if _, err := os.Stat(filepath); errors.Is(err, os.ErrNotExist) {
return false
}
return true
}
22 changes: 21 additions & 1 deletion images/plugin-compiler/data/build.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
#!/bin/bash
set -xe

CURRENTVERS=$(perl -n -e'/v(\d+).(\d+).(\d+)/'' && print "v$1\.$2\.$3"' $TYK_GW_PATH/gateway/version.go)
plugin_name=$1
plugin_id=$2
# GOOS and GOARCH can be send to override the name of the plugin
GOOS=$3
GOARCH=$4
CGOENABLED=0

PLUGIN_BUILD_PATH="/go/src/plugin_${plugin_name%.*}$plugin_id"

Expand All @@ -15,11 +20,26 @@ To build a plugin:
EOF
}

# if params were not send, then attempt to get them from env vars
if [[ $GOOS == "" ]] && [[ $GOARCH == "" ]]; then
GOOS=$(go env GOOS)
GOARCH=$(go env GOARCH)
fi

if [ -z "$plugin_name" ]; then
usage
exit 1
fi

# if arch and os present then update the name of file with those params
if [[ $GOOS != "" ]] && [[ $GOARCH != "" ]]; then
plugin_name="${plugin_name%.*}_${CURRENTVERS}_${GOOS}_${GOARCH}.so"
fi

if [[ $GOOS != "linux" ]];then
CGOENABLED=1
fi

mkdir -p $PLUGIN_BUILD_PATH
# Plugin's vendor folder, has precedence over the cached vendor'd dependencies from tyk
yes | cp -r $PLUGIN_SOURCE_PATH/* $PLUGIN_BUILD_PATH || true
Expand Down Expand Up @@ -53,5 +73,5 @@ rm -rf $TYK_GW_PATH/vendor

rm /go/src/modules.txt

GO111MODULE=off go build -buildmode=plugin -o $plugin_name \
GO111MODULE=off CGO_ENABLE=$CGO_ENABLE GOOS=$GOOS GOARCH=$GOARCH go build -buildmode=plugin -o $plugin_name \
&& mv $plugin_name $PLUGIN_SOURCE_PATH

0 comments on commit e60c6f2

Please sign in to comment.