2020-04-20 19:36:34 +03:00
package plugins
import (
"context"
"fmt"
"net/http"
2021-11-09 16:30:09 +03:00
"os"
2020-04-20 19:36:34 +03:00
2021-12-08 19:08:05 +03:00
"github.com/sirupsen/logrus"
"github.com/traefik/traefik/v2/pkg/log"
2020-09-16 16:46:04 +03:00
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
2020-04-20 19:36:34 +03:00
)
2020-09-07 14:58:03 +03:00
// Constructor creates a plugin handler.
type Constructor func ( context . Context , http . Handler ) ( http . Handler , error )
2020-04-20 19:36:34 +03:00
// Builder is a plugin builder.
type Builder struct {
2022-01-14 14:22:06 +03:00
middlewareBuilders map [ string ] * middlewareBuilder
providerBuilders map [ string ] providerBuilder
2020-04-20 19:36:34 +03:00
}
// NewBuilder creates a new Builder.
2021-06-25 16:50:09 +03:00
func NewBuilder ( client * Client , plugins map [ string ] Descriptor , localPlugins map [ string ] LocalDescriptor ) ( * Builder , error ) {
2020-04-20 19:36:34 +03:00
pb := & Builder {
2022-01-14 14:22:06 +03:00
middlewareBuilders : map [ string ] * middlewareBuilder { } ,
providerBuilders : map [ string ] providerBuilder { } ,
2020-04-20 19:36:34 +03:00
}
for pName , desc := range plugins {
manifest , err := client . ReadManifest ( desc . ModuleName )
if err != nil {
_ = client . ResetAll ( )
return nil , fmt . Errorf ( "%s: failed to read manifest: %w" , desc . ModuleName , err )
}
2021-12-08 19:08:05 +03:00
logger := log . WithoutContext ( ) . WithFields ( logrus . Fields { "plugin" : "plugin-" + pName , "module" : desc . ModuleName } )
i := interp . New ( interp . Options {
GoPath : client . GoPath ( ) ,
Env : os . Environ ( ) ,
Stdout : logger . WriterLevel ( logrus . DebugLevel ) ,
Stderr : logger . WriterLevel ( logrus . ErrorLevel ) ,
} )
2021-06-25 16:50:09 +03:00
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 )
}
2020-04-20 19:36:34 +03:00
_ , err = i . Eval ( fmt . Sprintf ( ` import "%s" ` , manifest . Import ) )
if err != nil {
return nil , fmt . Errorf ( "%s: failed to import plugin code %q: %w" , desc . ModuleName , manifest . Import , err )
}
2021-05-11 17:14:10 +03:00
switch manifest . Type {
case "middleware" :
2022-01-14 14:22:06 +03:00
middleware , err := newMiddlewareBuilder ( i , manifest . BasePkg , manifest . Import )
if err != nil {
return nil , err
2021-05-11 17:14:10 +03:00
}
2022-01-14 14:22:06 +03:00
pb . middlewareBuilders [ pName ] = middleware
2021-05-11 17:14:10 +03:00
case "provider" :
2022-01-14 14:22:06 +03:00
pb . providerBuilders [ pName ] = providerBuilder {
2021-05-11 17:14:10 +03:00
interpreter : i ,
Import : manifest . Import ,
BasePkg : manifest . BasePkg ,
}
default :
return nil , fmt . Errorf ( "unknow plugin type: %s" , manifest . Type )
2020-04-20 19:36:34 +03:00
}
}
2021-06-25 16:50:09 +03:00
for pName , desc := range localPlugins {
manifest , err := ReadManifest ( localGoPath , desc . ModuleName )
2020-04-20 19:36:34 +03:00
if err != nil {
2021-06-25 16:50:09 +03:00
return nil , fmt . Errorf ( "%s: failed to read manifest: %w" , desc . ModuleName , err )
2020-04-20 19:36:34 +03:00
}
2021-12-08 19:08:05 +03:00
logger := log . WithoutContext ( ) . WithFields ( logrus . Fields { "plugin" : "plugin-" + pName , "module" : desc . ModuleName } )
i := interp . New ( interp . Options {
GoPath : localGoPath ,
Env : os . Environ ( ) ,
Stdout : logger . WriterLevel ( logrus . DebugLevel ) ,
Stderr : logger . WriterLevel ( logrus . ErrorLevel ) ,
} )
2021-06-25 16:50:09 +03:00
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 )
}
2020-04-20 19:36:34 +03:00
_ , err = i . Eval ( fmt . Sprintf ( ` import "%s" ` , manifest . Import ) )
if err != nil {
2021-06-25 16:50:09 +03:00
return nil , fmt . Errorf ( "%s: failed to import plugin code %q: %w" , desc . ModuleName , manifest . Import , err )
2020-04-20 19:36:34 +03:00
}
2021-05-11 17:14:10 +03:00
switch manifest . Type {
case "middleware" :
2022-01-14 14:22:06 +03:00
middleware , err := newMiddlewareBuilder ( i , manifest . BasePkg , manifest . Import )
if err != nil {
return nil , err
2021-05-11 17:14:10 +03:00
}
2022-01-14 14:22:06 +03:00
pb . middlewareBuilders [ pName ] = middleware
2021-05-11 17:14:10 +03:00
case "provider" :
2022-01-14 14:22:06 +03:00
pb . providerBuilders [ pName ] = providerBuilder {
2021-05-11 17:14:10 +03:00
interpreter : i ,
Import : manifest . Import ,
BasePkg : manifest . BasePkg ,
}
default :
return nil , fmt . Errorf ( "unknow plugin type: %s" , manifest . Type )
2020-04-20 19:36:34 +03:00
}
}
return pb , nil
}