Skip to content
Merged
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
18 changes: 16 additions & 2 deletions cmd/config_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func getPath(path string) string {
// return nil
// }

// verificationPolicy returns the verification policy for the plugin from config file.
// verificationPolicy returns the hook verification policy from plugin config file.
func verificationPolicy() plugin.Policy {
vPolicy := globalConfig.String("plugins.verificationPolicy")
vPolicy := pluginConfig.String("plugins.verificationPolicy")
verificationPolicy := plugin.PassDown // default
switch vPolicy {
case "ignore":
Expand All @@ -52,6 +52,20 @@ func verificationPolicy() plugin.Policy {
return verificationPolicy
}

// pluginCompatPolicy returns the plugin compatibility policy from plugin config file.
func pluginCompatPolicy() plugin.CompatPolicy {
vPolicy := pluginConfig.String("plugins.compatibilityPolicy")
compatPolicy := plugin.Strict // default
switch vPolicy {
case "strict":
compatPolicy = plugin.Strict
case "loose":
compatPolicy = plugin.Loose
}

return compatPolicy
}

// loggerConfig returns the logger config from config file.
func loggerConfig() logging.LoggerConfig {
cfg := logging.LoggerConfig{StartupMsg: true}
Expand Down
27 changes: 15 additions & 12 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ var runCmd = &cobra.Command{
}
}

// Load plugins and register their hooks
// Set the plugin compatibility policy.
pluginRegistry.CompatPolicy = pluginCompatPolicy()

// Load plugins and register their hooks.
pluginRegistry.LoadPlugins(pluginConfig)

if f, err := cmd.Flags().GetString("config"); err == nil {
Expand All @@ -68,7 +71,7 @@ var runCmd = &cobra.Command{
}
}

// Get hooks signature verification policy
// Get hooks signature verification policy.
hooksConfig.Verification = verificationPolicy()

// The config will be passed to the hooks, and in turn to the plugins that
Expand All @@ -91,11 +94,11 @@ var runCmd = &cobra.Command{
}
}

// Create a new logger from the config
// Create a new logger from the config.
loggerCfg := loggerConfig()
logger := logging.NewLogger(loggerCfg)

// Replace the default logger with the new one from the config
// Replace the default logger with the new one from the config.
hooksConfig.Logger = logger

// This is a notification hook, so we don't care about the result.
Expand All @@ -111,7 +114,7 @@ var runCmd = &cobra.Command{
logger.Error().Err(err).Msg("Failed to run OnNewLogger hooks")
}

// Create and initialize a pool of connections
// Create and initialize a pool of connections.
poolSize, clientConfig := poolConfig()
pool := pool.NewPool(poolSize)

Expand All @@ -136,10 +139,10 @@ var runCmd = &cobra.Command{
"address": clientConfig.Address,
"receiveBufferSize": clientConfig.ReceiveBufferSize,
"receiveChunkSize": clientConfig.ReceiveChunkSize,
"receiveDeadline": clientConfig.ReceiveDeadline,
"sendDeadline": clientConfig.SendDeadline,
"receiveDeadline": clientConfig.ReceiveDeadline.Seconds(),
"sendDeadline": clientConfig.SendDeadline.Seconds(),
"tcpKeepAlive": clientConfig.TCPKeepAlive,
"tcpKeepAlivePeriod": clientConfig.TCPKeepAlivePeriod,
"tcpKeepAlivePeriod": clientConfig.TCPKeepAlivePeriod.Seconds(),
}
_, err := hooksConfig.Run(
context.Background(),
Expand Down Expand Up @@ -178,7 +181,7 @@ var runCmd = &cobra.Command{
logger.Error().Err(err).Msg("Failed to run OnNewPool hooks")
}

// Create a prefork proxy with the pool of clients
// Create a prefork proxy with the pool of clients.
elastic, reuseElasticClients, elasticClientConfig := proxyConfig()
proxy := network.NewProxy(
pool, hooksConfig, elastic, reuseElasticClients, elasticClientConfig, logger)
Expand Down Expand Up @@ -266,7 +269,7 @@ var runCmd = &cobra.Command{
logger.Error().Err(err).Msg("Failed to run OnNewServer hooks")
}

// Shutdown the server gracefully
// Shutdown the server gracefully.
var signals []os.Signal
signals = append(signals,
os.Interrupt,
Expand All @@ -283,7 +286,7 @@ var runCmd = &cobra.Command{
for sig := range signalsCh {
for _, s := range signals {
if sig != s {
// Notify the hooks that the server is shutting down
// Notify the hooks that the server is shutting down.
_, err := hooksConfig.Run(
context.Background(),
map[string]interface{}{"signal": sig.String()},
Expand All @@ -302,7 +305,7 @@ var runCmd = &cobra.Command{
}
}(hooksConfig)

// Run the server
// Run the server.
if err := server.Run(); err != nil {
logger.Error().Err(err).Msg("Failed to start server")
}
Expand Down
3 changes: 0 additions & 3 deletions gatewayd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,3 @@ server:
reusePort: True
tcpKeepAlive: 3s # duration
tcpNoDelay: True

plugins:
verificationPolicy: "passdown"
8 changes: 7 additions & 1 deletion gatewayd_plugins.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Plugin configuration file for GatewayD

plugins:
# Possible values: "passdown" (default), "ignore", "abort" and "remove"
verificationPolicy: "passdown"
# Possible values: "strict" (default) and "loose"
compatibilityPolicy: "strict"

# Plugin name
gatewayd-plugin-test:
# whether to enable or disable the plugin on the next run
Expand All @@ -17,4 +23,4 @@ gatewayd-plugin-test:
- MAGIC_COOKIE_KEY=GATEWAYD_PLUGIN
- MAGIC_COOKIE_VALUE=5712b87aa5d7e9f9e9ab643e6603181c5b796015cb1c09d6f5ada882bf2a1872
# Checksum hash to verify the binary before loading
checksum: 006e19bcfd1951e077746143590934470e8b1d67a3904036603013e150fcb708
checksum: 09bce5ad90a36a6f3ec0804023098519424289f3d68679159d68e0b08ce70c89
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/gatewayd-io/gatewayd
go 1.19

require (
github.com/Masterminds/semver/v3 v3.2.0
github.com/fergusstrange/embedded-postgres v1.19.0
github.com/google/go-cmp v0.5.9
github.com/hashicorp/go-hclog v1.4.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand Down
99 changes: 93 additions & 6 deletions plugin/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package plugin
import (
"context"

semver "github.com/Masterminds/semver/v3"
gerr "github.com/gatewayd-io/gatewayd/errors"
"github.com/gatewayd-io/gatewayd/logging"
pluginV1 "github.com/gatewayd-io/gatewayd/plugin/v1"
Expand All @@ -13,6 +14,8 @@ import (
"google.golang.org/protobuf/types/known/structpb"
)

type CompatPolicy uint

const (
DefaultMinPort uint = 50000
DefaultMaxPort uint = 60000
Expand All @@ -21,19 +24,26 @@ const (
LoggerName string = "plugin"
)

const (
Strict CompatPolicy = iota
Loose
)

type Registry interface {
Add(plugin *Impl) bool
Get(id Identifier) *Impl
List() []Identifier
Exists(name, version, remoteURL string) bool
Remove(id Identifier)
Shutdown()
LoadPlugins(pluginConfig *koanf.Koanf)
RegisterHooks(id Identifier)
}

type RegistryImpl struct {
plugins pool.Pool
hooksConfig *HookConfig
plugins pool.Pool
hooksConfig *HookConfig
CompatPolicy CompatPolicy
}

var _ Registry = &RegistryImpl{}
Expand Down Expand Up @@ -74,6 +84,40 @@ func (reg *RegistryImpl) List() []Identifier {
return plugins
}

// Exists checks if a plugin exists in the registry.
func (reg *RegistryImpl) Exists(name, version, remoteURL string) bool {
for _, plugin := range reg.List() {
if plugin.Name == name && plugin.RemoteURL == remoteURL {
// Parse the supplied version and the version in the registry.
suppliedVer, err := semver.NewVersion(version)
if err != nil {
reg.hooksConfig.Logger.Error().Err(err).Msg(
"Failed to parse supplied plugin version")
return false
}

registryVer, err := semver.NewVersion(plugin.Version)
if err != nil {
reg.hooksConfig.Logger.Error().Err(err).Msg(
"Failed to parse plugin version in registry")
return false
}

// Check if the version of the plugin is less than or equal to
// the version in the registry.
if suppliedVer.LessThan(registryVer) || suppliedVer.Equal(registryVer) {
return true
}

reg.hooksConfig.Logger.Debug().Str("name", name).Str("version", version).Msg(
"Supplied plugin version is greater than the version in registry")
return false
}
}

return false
}

// Remove removes a plugin from the registry.
func (reg *RegistryImpl) Remove(id Identifier) {
reg.plugins.Remove(id)
Expand Down Expand Up @@ -105,6 +149,11 @@ func (reg *RegistryImpl) LoadPlugins(pluginConfig *koanf.Koanf) {

// Add each plugin to the registry.
for priority, name := range plugins {
// Skip the top-level "plugins" key.
if name == "plugins" {
continue
}

reg.hooksConfig.Logger.Debug().Str("name", name).Msg("Loading plugin")
plugin := &Impl{
ID: Identifier{
Expand Down Expand Up @@ -209,24 +258,62 @@ func (reg *RegistryImpl) LoadPlugins(pluginConfig *koanf.Koanf) {
}
}

// Retrieve plugin requirements.
if err := mapstructure.Decode(metadata.Fields["requires"].GetListValue().AsSlice(),
&plugin.Requires); err != nil {
reg.hooksConfig.Logger.Debug().Err(err).Msg("Failed to decode plugin requirements")
}

// Too many requirements or not enough plugins loaded.
if len(plugin.Requires) > reg.plugins.Size() {
reg.hooksConfig.Logger.Debug().Msg(
"The plugin has too many requirements, " +
"and not enough of them exist in the registry, so it won't work properly")
}

// Check if the plugin requirements are met.
for _, req := range plugin.Requires {
if !reg.Exists(req.Name, req.Version, req.RemoteURL) {
reg.hooksConfig.Logger.Debug().Fields(
map[string]interface{}{
"name": plugin.ID.Name,
"requirement": req.Name,
},
).Msg("The plugin requirement is not met, so it won't work properly")
if reg.CompatPolicy == Strict {
reg.hooksConfig.Logger.Debug().Str("name", plugin.ID.Name).Msg(
"Registry is in strict compatibility mode, so the plugin won't be loaded")
plugin.Stop() // Stop the plugin.
continue
} else {
reg.hooksConfig.Logger.Debug().Fields(
map[string]interface{}{
"name": plugin.ID.Name,
"requirement": req.Name,
},
).Msg("Registry is in loose compatibility mode, " +
"so the plugin will be loaded anyway")
}
}
}

plugin.ID.RemoteURL = metadata.Fields["id"].GetStructValue().Fields["remoteUrl"].GetStringValue()
plugin.ID.Version = metadata.Fields["id"].GetStructValue().Fields["version"].GetStringValue()
plugin.Description = metadata.Fields["description"].GetStringValue()
plugin.License = metadata.Fields["license"].GetStringValue()
plugin.ProjectURL = metadata.Fields["projectUrl"].GetStringValue()
if err := mapstructure.Decode(metadata.Fields["requires"].GetListValue().AsSlice(),
&plugin.Requires); err != nil {
reg.hooksConfig.Logger.Debug().Err(err).Msg("Failed to decode plugin requirements")
}
// Retrieve authors.
if err := mapstructure.Decode(metadata.Fields["authors"].GetListValue().AsSlice(),
&plugin.Authors); err != nil {
reg.hooksConfig.Logger.Debug().Err(err).Msg("Failed to decode plugin authors")
}
// Retrieve hooks.
if err := mapstructure.Decode(metadata.Fields["hooks"].GetListValue().AsSlice(),
&plugin.Hooks); err != nil {
reg.hooksConfig.Logger.Debug().Err(err).Msg("Failed to decode plugin hooks")
}

// Retrieve plugin config.
plugin.Config = make(map[string]string)
for key, value := range metadata.Fields["config"].GetStructValue().AsMap() {
if val, ok := value.(string); ok {
Expand Down
28 changes: 14 additions & 14 deletions plugin/v1/plugin.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading