Local private plugins.
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
This commit is contained in:
parent
a243ac4dde
commit
5e3e47b484
11 changed files with 196 additions and 123 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,4 +17,5 @@
|
||||||
cover.out
|
cover.out
|
||||||
vendor/
|
vendor/
|
||||||
plugins-storage/
|
plugins-storage/
|
||||||
|
plugins-local/
|
||||||
traefik_changelog.md
|
traefik_changelog.md
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||||
)
|
)
|
||||||
|
@ -8,35 +10,69 @@ import (
|
||||||
const outputDir = "./plugins-storage/"
|
const outputDir = "./plugins-storage/"
|
||||||
|
|
||||||
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
||||||
client, plgs, devPlugin, err := initPlugins(staticConfiguration)
|
client, plgs, localPlgs, err := initPlugins(staticConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugins.NewBuilder(client, plgs, devPlugin)
|
return plugins.NewBuilder(client, plgs, localPlgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, *plugins.DevPlugin, error) {
|
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) {
|
||||||
if !isPilotEnabled(staticCfg) || !hasPlugins(staticCfg) {
|
err := checkUniquePluginNames(staticCfg.Experimental)
|
||||||
return nil, map[string]plugins.Descriptor{}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := plugins.ClientOptions{
|
|
||||||
Output: outputDir,
|
|
||||||
Token: staticCfg.Pilot.Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := plugins.NewClient(opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = plugins.Setup(client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin)
|
var client *plugins.Client
|
||||||
if err != nil {
|
plgs := map[string]plugins.Descriptor{}
|
||||||
return nil, nil, nil, err
|
|
||||||
|
if isPilotEnabled(staticCfg) && hasPlugins(staticCfg) {
|
||||||
|
opts := plugins.ClientOptions{
|
||||||
|
Output: outputDir,
|
||||||
|
Token: staticCfg.Pilot.Token,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
client, err = plugins.NewClient(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = plugins.SetupRemotePlugins(client, staticCfg.Experimental.Plugins)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
plgs = staticCfg.Experimental.Plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin, nil
|
localPlgs := map[string]plugins.LocalDescriptor{}
|
||||||
|
|
||||||
|
if hasLocalPlugins(staticCfg) {
|
||||||
|
err := plugins.SetupLocalPlugins(staticCfg.Experimental.LocalPlugins)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
localPlgs = staticCfg.Experimental.LocalPlugins
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, plgs, localPlgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUniquePluginNames(e *static.Experimental) error {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for s := range e.LocalPlugins {
|
||||||
|
if _, ok := e.Plugins[s]; ok {
|
||||||
|
return fmt.Errorf("the plugin's name %q must be unique", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPilotEnabled(staticCfg *static.Configuration) bool {
|
func isPilotEnabled(staticCfg *static.Configuration) bool {
|
||||||
|
@ -44,6 +80,9 @@ func isPilotEnabled(staticCfg *static.Configuration) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasPlugins(staticCfg *static.Configuration) bool {
|
func hasPlugins(staticCfg *static.Configuration) bool {
|
||||||
return staticCfg.Experimental != nil &&
|
return staticCfg.Experimental != nil && len(staticCfg.Experimental.Plugins) > 0
|
||||||
(len(staticCfg.Experimental.Plugins) > 0 || staticCfg.Experimental.DevPlugin != nil)
|
}
|
||||||
|
|
||||||
|
func hasLocalPlugins(staticCfg *static.Configuration) bool {
|
||||||
|
return staticCfg.Experimental != nil && len(staticCfg.Experimental.LocalPlugins) > 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,12 +126,6 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||||
|
|
||||||
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.DevPlugin != nil {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithTimeout(ctx, 30*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
if staticConfiguration.Ping != nil {
|
if staticConfiguration.Ping != nil {
|
||||||
staticConfiguration.Ping.WithContext(ctx)
|
staticConfiguration.Ping.WithContext(ctx)
|
||||||
}
|
}
|
||||||
|
@ -240,8 +234,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||||
|
|
||||||
// Providers plugins
|
// Providers plugins
|
||||||
|
|
||||||
for s, i := range staticConfiguration.Providers.Plugin {
|
for name, conf := range staticConfiguration.Providers.Plugin {
|
||||||
p, err := pluginBuilder.BuildProvider(s, i)
|
p, err := pluginBuilder.BuildProvider(name, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("plugin: failed to build provider: %w", err)
|
return nil, fmt.Errorf("plugin: failed to build provider: %w", err)
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -71,7 +71,7 @@ require (
|
||||||
github.com/tinylib/msgp v1.0.2 // indirect
|
github.com/tinylib/msgp v1.0.2 // indirect
|
||||||
github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888
|
github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888
|
||||||
github.com/traefik/paerser v0.1.4
|
github.com/traefik/paerser v0.1.4
|
||||||
github.com/traefik/yaegi v0.9.17
|
github.com/traefik/yaegi v0.9.19
|
||||||
github.com/uber/jaeger-client-go v2.29.1+incompatible
|
github.com/uber/jaeger-client-go v2.29.1+incompatible
|
||||||
github.com/uber/jaeger-lib v2.2.0+incompatible
|
github.com/uber/jaeger-lib v2.2.0+incompatible
|
||||||
github.com/unrolled/render v1.0.2
|
github.com/unrolled/render v1.0.2
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1020,8 +1020,8 @@ github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888 h1:GMY0C+M/w
|
||||||
github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888/go.mod h1:sLqwoN03tkluITKL+lPEZbfsJQU2suYoKbrR/HeV9aM=
|
github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888/go.mod h1:sLqwoN03tkluITKL+lPEZbfsJQU2suYoKbrR/HeV9aM=
|
||||||
github.com/traefik/paerser v0.1.4 h1:/IXjV04Gf6di51H8Jl7jyS3OylsLjIasrwXIIwj1aT8=
|
github.com/traefik/paerser v0.1.4 h1:/IXjV04Gf6di51H8Jl7jyS3OylsLjIasrwXIIwj1aT8=
|
||||||
github.com/traefik/paerser v0.1.4/go.mod h1:FIdQ4Y92ulQUGSeZgxchtBKEcLw1o551PMNg9PoIq/4=
|
github.com/traefik/paerser v0.1.4/go.mod h1:FIdQ4Y92ulQUGSeZgxchtBKEcLw1o551PMNg9PoIq/4=
|
||||||
github.com/traefik/yaegi v0.9.17 h1:sJ4Wk6S7HHHXtJnOuxC/3qjdQKRy3q9ZhNP0ZGL7Ltw=
|
github.com/traefik/yaegi v0.9.19 h1:ze01+pVtKmxSogy0wlAPSvm2LoDYuZj2LdH3S6GxHcQ=
|
||||||
github.com/traefik/yaegi v0.9.17/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk=
|
github.com/traefik/yaegi v0.9.19/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk=
|
||||||
github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE=
|
github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE=
|
||||||
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
|
|
|
@ -958,9 +958,13 @@ func TestDo_staticConfiguration(t *testing.T) {
|
||||||
Version: "foobar",
|
Version: "foobar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DevPlugin: &plugins.DevPlugin{
|
LocalPlugins: map[string]plugins.LocalDescriptor{
|
||||||
GoPath: "foobar",
|
"Descriptor0": {
|
||||||
ModuleName: "foobar",
|
ModuleName: "foobar",
|
||||||
|
},
|
||||||
|
"Descriptor1": {
|
||||||
|
ModuleName: "foobar",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -456,9 +456,13 @@
|
||||||
"version": "foobar"
|
"version": "foobar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devPlugin": {
|
"localPlugins": {
|
||||||
"goPath": "foobar",
|
"Descriptor0": {
|
||||||
"moduleName": "foobar"
|
"moduleName": "foobar"
|
||||||
|
},
|
||||||
|
"Descriptor1": {
|
||||||
|
"moduleName": "foobar"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,9 @@ import "github.com/traefik/traefik/v2/pkg/plugins"
|
||||||
|
|
||||||
// Experimental experimental Traefik features.
|
// Experimental experimental Traefik features.
|
||||||
type Experimental struct {
|
type Experimental struct {
|
||||||
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"`
|
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"`
|
||||||
DevPlugin *plugins.DevPlugin `description:"Dev plugin configuration." json:"devPlugin,omitempty" toml:"devPlugin,omitempty" yaml:"devPlugin,omitempty" export:"true"`
|
LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"`
|
||||||
KubernetesGateway bool `description:"Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
|
|
||||||
HTTP3 bool `description:"Enable HTTP3." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" export:"true"`
|
KubernetesGateway bool `description:"Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
|
||||||
|
HTTP3 bool `description:"Enable HTTP3." json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"github.com/traefik/yaegi/stdlib"
|
"github.com/traefik/yaegi/stdlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const devPluginName = "dev"
|
|
||||||
|
|
||||||
// Constructor creates a plugin handler.
|
// Constructor creates a plugin handler.
|
||||||
type Constructor func(context.Context, http.Handler) (http.Handler, error)
|
type Constructor func(context.Context, http.Handler) (http.Handler, error)
|
||||||
|
|
||||||
|
@ -35,7 +33,7 @@ type Builder struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuilder creates a new Builder.
|
// NewBuilder creates a new Builder.
|
||||||
func NewBuilder(client *Client, plugins map[string]Descriptor, devPlugin *DevPlugin) (*Builder, error) {
|
func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[string]LocalDescriptor) (*Builder, error) {
|
||||||
pb := &Builder{
|
pb := &Builder{
|
||||||
middlewareDescriptors: map[string]pluginContext{},
|
middlewareDescriptors: map[string]pluginContext{},
|
||||||
providerDescriptors: map[string]pluginContext{},
|
providerDescriptors: map[string]pluginContext{},
|
||||||
|
@ -49,8 +47,16 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, devPlugin *DevPlu
|
||||||
}
|
}
|
||||||
|
|
||||||
i := interp.New(interp.Options{GoPath: client.GoPath()})
|
i := interp.New(interp.Options{GoPath: client.GoPath()})
|
||||||
i.Use(stdlib.Symbols)
|
|
||||||
i.Use(ppSymbols())
|
err = i.Use(stdlib.Symbols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: failed to load symbols: %w", desc.ModuleName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i.Use(ppSymbols())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: failed to load provider symbols: %w", desc.ModuleName, err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = i.Eval(fmt.Sprintf(`import "%s"`, manifest.Import))
|
_, err = i.Eval(fmt.Sprintf(`import "%s"`, manifest.Import))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,33 +83,41 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, devPlugin *DevPlu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if devPlugin != nil {
|
for pName, desc := range localPlugins {
|
||||||
manifest, err := ReadManifest(devPlugin.GoPath, devPlugin.ModuleName)
|
manifest, err := ReadManifest(localGoPath, desc.ModuleName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s: failed to read manifest: %w", devPlugin.ModuleName, err)
|
return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
i := interp.New(interp.Options{GoPath: devPlugin.GoPath})
|
i := interp.New(interp.Options{GoPath: localGoPath})
|
||||||
i.Use(stdlib.Symbols)
|
|
||||||
i.Use(ppSymbols())
|
err = i.Use(stdlib.Symbols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: failed to load symbols: %w", desc.ModuleName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i.Use(ppSymbols())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: failed to load provider symbols: %w", desc.ModuleName, err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = i.Eval(fmt.Sprintf(`import "%s"`, manifest.Import))
|
_, err = i.Eval(fmt.Sprintf(`import "%s"`, manifest.Import))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s: failed to import plugin code %q: %w", devPlugin.ModuleName, manifest.Import, err)
|
return nil, fmt.Errorf("%s: failed to import plugin code %q: %w", desc.ModuleName, manifest.Import, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch manifest.Type {
|
switch manifest.Type {
|
||||||
case "middleware":
|
case "middleware":
|
||||||
pb.middlewareDescriptors[devPluginName] = pluginContext{
|
pb.middlewareDescriptors[pName] = pluginContext{
|
||||||
interpreter: i,
|
interpreter: i,
|
||||||
GoPath: devPlugin.GoPath,
|
GoPath: localGoPath,
|
||||||
Import: manifest.Import,
|
Import: manifest.Import,
|
||||||
BasePkg: manifest.BasePkg,
|
BasePkg: manifest.BasePkg,
|
||||||
}
|
}
|
||||||
case "provider":
|
case "provider":
|
||||||
pb.providerDescriptors[devPluginName] = pluginContext{
|
pb.providerDescriptors[pName] = pluginContext{
|
||||||
interpreter: i,
|
interpreter: i,
|
||||||
GoPath: devPlugin.GoPath,
|
GoPath: localGoPath,
|
||||||
Import: manifest.Import,
|
Import: manifest.Import,
|
||||||
BasePkg: manifest.BasePkg,
|
BasePkg: manifest.BasePkg,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setup setup plugins environment.
|
const localGoPath = "./plugins-local/"
|
||||||
func Setup(client *Client, plugins map[string]Descriptor, devPlugin *DevPlugin) error {
|
|
||||||
err := checkPluginsConfiguration(plugins)
|
// SetupRemotePlugins setup remote plugins environment.
|
||||||
|
func SetupRemotePlugins(client *Client, plugins map[string]Descriptor) error {
|
||||||
|
err := checkRemotePluginsConfiguration(plugins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid configuration: %w", err)
|
return fmt.Errorf("invalid configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -53,65 +56,10 @@ func Setup(client *Client, plugins map[string]Descriptor, devPlugin *DevPlugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if devPlugin != nil {
|
|
||||||
err := checkDevPluginConfiguration(devPlugin)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid configuration: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDevPluginConfiguration(plugin *DevPlugin) error {
|
func checkRemotePluginsConfiguration(plugins map[string]Descriptor) error {
|
||||||
if plugin == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if plugin.GoPath == "" {
|
|
||||||
return errors.New("missing Go Path (prefer a dedicated Go Path)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if plugin.ModuleName == "" {
|
|
||||||
return errors.New("missing module name")
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := ReadManifest(plugin.GoPath, plugin.ModuleName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m.Type {
|
|
||||||
case "middleware", "provider":
|
|
||||||
// noop
|
|
||||||
default:
|
|
||||||
return errors.New("unsupported type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Import == "" {
|
|
||||||
return errors.New("missing import")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(m.Import, plugin.ModuleName) {
|
|
||||||
return fmt.Errorf("the import %q must be related to the module name %q", m.Import, plugin.ModuleName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.DisplayName == "" {
|
|
||||||
return errors.New("missing DisplayName")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Summary == "" {
|
|
||||||
return errors.New("missing Summary")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.TestData == nil {
|
|
||||||
return errors.New("missing TestData")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPluginsConfiguration(plugins map[string]Descriptor) error {
|
|
||||||
if plugins == nil {
|
if plugins == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -147,3 +95,74 @@ func checkPluginsConfiguration(plugins map[string]Descriptor) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupLocalPlugins setup local plugins environment.
|
||||||
|
func SetupLocalPlugins(plugins map[string]LocalDescriptor) error {
|
||||||
|
if plugins == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uniq := make(map[string]struct{})
|
||||||
|
|
||||||
|
var errs *multierror.Error
|
||||||
|
for pAlias, descriptor := range plugins {
|
||||||
|
if descriptor.ModuleName == "" {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: plugin name is missing", pAlias))
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(descriptor.ModuleName, "/") || strings.HasSuffix(descriptor.ModuleName, "/") {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: plugin name should not start or end with a /", pAlias))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := uniq[descriptor.ModuleName]; ok {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("only one version of a plugin is allowed, there is a duplicate of %s", descriptor.ModuleName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
uniq[descriptor.ModuleName] = struct{}{}
|
||||||
|
|
||||||
|
err := checkLocalPluginManifest(descriptor)
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs.ErrorOrNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLocalPluginManifest(descriptor LocalDescriptor) error {
|
||||||
|
m, err := ReadManifest(localGoPath, descriptor.ModuleName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs *multierror.Error
|
||||||
|
|
||||||
|
switch m.Type {
|
||||||
|
case "middleware", "provider":
|
||||||
|
// noop
|
||||||
|
default:
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: unsupported type %q", descriptor.ModuleName, m.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Import == "" {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: missing import", descriptor.ModuleName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(m.Import, descriptor.ModuleName) {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("the import %q must be related to the module name %q", m.Import, descriptor.ModuleName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.DisplayName == "" {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: missing DisplayName", descriptor.ModuleName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Summary == "" {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: missing Summary", descriptor.ModuleName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.TestData == nil {
|
||||||
|
errs = multierror.Append(errs, fmt.Errorf("%s: missing TestData", descriptor.ModuleName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs.ErrorOrNil()
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
// Descriptor The static part of a plugin configuration (prod).
|
// Descriptor The static part of a plugin configuration.
|
||||||
type Descriptor struct {
|
type Descriptor struct {
|
||||||
// ModuleName (required)
|
// ModuleName (required)
|
||||||
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
|
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
|
||||||
|
@ -9,13 +9,10 @@ type Descriptor struct {
|
||||||
Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"`
|
Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DevPlugin The static part of a plugin configuration (only for dev).
|
// LocalDescriptor The static part of a local plugin configuration.
|
||||||
type DevPlugin struct {
|
type LocalDescriptor struct {
|
||||||
// GoPath plugin's GOPATH. (required)
|
|
||||||
GoPath string `description:"plugin's GOPATH." json:"goPath,omitempty" toml:"goPath,omitempty" yaml:"goPath,omitempty" export:"true"`
|
|
||||||
|
|
||||||
// ModuleName (required)
|
// ModuleName (required)
|
||||||
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
|
ModuleName string `description:"plugin's module name." json:"moduleName,omitempty" toml:"moduleName,omitempty" yaml:"moduleName,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manifest The plugin manifest.
|
// Manifest The plugin manifest.
|
||||||
|
|
Loading…
Reference in a new issue