mirror of
				https://github.com/traefik/traefik.git
				synced 2025-10-31 08:21:27 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			185 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package plugins
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"path/filepath"
 | |
| 
 | |
| 	"github.com/rs/zerolog/log"
 | |
| )
 | |
| 
 | |
| // Constructor creates a plugin handler.
 | |
| type Constructor func(context.Context, http.Handler) (http.Handler, error)
 | |
| 
 | |
| type pluginMiddleware interface {
 | |
| 	NewHandler(ctx context.Context, next http.Handler) (http.Handler, error)
 | |
| }
 | |
| 
 | |
| type middlewareBuilder interface {
 | |
| 	newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error)
 | |
| }
 | |
| 
 | |
| // Builder is a plugin builder.
 | |
| type Builder struct {
 | |
| 	providerBuilders   map[string]providerBuilder
 | |
| 	middlewareBuilders map[string]middlewareBuilder
 | |
| }
 | |
| 
 | |
| // NewBuilder creates a new Builder.
 | |
| func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins map[string]LocalDescriptor) (*Builder, error) {
 | |
| 	ctx := context.Background()
 | |
| 
 | |
| 	pb := &Builder{
 | |
| 		middlewareBuilders: map[string]middlewareBuilder{},
 | |
| 		providerBuilders:   map[string]providerBuilder{},
 | |
| 	}
 | |
| 
 | |
| 	for pName, desc := range plugins {
 | |
| 		manifest, err := manager.ReadManifest(desc.ModuleName)
 | |
| 		if err != nil {
 | |
| 			_ = manager.ResetAll()
 | |
| 			return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
 | |
| 		}
 | |
| 
 | |
| 		logger := log.With().
 | |
| 			Str("plugin", "plugin-"+pName).
 | |
| 			Str("module", desc.ModuleName).
 | |
| 			Str("runtime", manifest.Runtime).
 | |
| 			Logger()
 | |
| 		logCtx := logger.WithContext(ctx)
 | |
| 
 | |
| 		switch manifest.Type {
 | |
| 		case typeMiddleware:
 | |
| 			middleware, err := newMiddlewareBuilder(logCtx, manager.GoPath(), manifest, desc.ModuleName, desc.Settings)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			pb.middlewareBuilders[pName] = middleware
 | |
| 
 | |
| 		case typeProvider:
 | |
| 			pBuilder, err := newProviderBuilder(logCtx, manifest, manager.GoPath(), desc.Settings)
 | |
| 			if err != nil {
 | |
| 				return nil, fmt.Errorf("%s: %w", desc.ModuleName, err)
 | |
| 			}
 | |
| 
 | |
| 			pb.providerBuilders[pName] = pBuilder
 | |
| 
 | |
| 		default:
 | |
| 			return nil, fmt.Errorf("unknow plugin type: %s", manifest.Type)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for pName, desc := range localPlugins {
 | |
| 		manifest, err := ReadManifest(localGoPath, desc.ModuleName)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
 | |
| 		}
 | |
| 
 | |
| 		logger := log.With().
 | |
| 			Str("plugin", "plugin-"+pName).
 | |
| 			Str("module", desc.ModuleName).
 | |
| 			Str("runtime", manifest.Runtime).
 | |
| 			Logger()
 | |
| 		logCtx := logger.WithContext(ctx)
 | |
| 
 | |
| 		switch manifest.Type {
 | |
| 		case typeMiddleware:
 | |
| 			middleware, err := newMiddlewareBuilder(logCtx, localGoPath, manifest, desc.ModuleName, desc.Settings)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			pb.middlewareBuilders[pName] = middleware
 | |
| 
 | |
| 		case typeProvider:
 | |
| 			builder, err := newProviderBuilder(logCtx, manifest, localGoPath, desc.Settings)
 | |
| 			if err != nil {
 | |
| 				return nil, fmt.Errorf("%s: %w", desc.ModuleName, err)
 | |
| 			}
 | |
| 
 | |
| 			pb.providerBuilders[pName] = builder
 | |
| 
 | |
| 		default:
 | |
| 			return nil, fmt.Errorf("unknow plugin type: %s", manifest.Type)
 | |
| 		}
 | |
| 	}
 | |
| 	return pb, nil
 | |
| }
 | |
| 
 | |
| // Build builds a middleware plugin.
 | |
| func (b Builder) Build(pName string, config map[string]interface{}, middlewareName string) (Constructor, error) {
 | |
| 	if b.middlewareBuilders == nil {
 | |
| 		return nil, fmt.Errorf("no plugin definitions in the static configuration: %s", pName)
 | |
| 	}
 | |
| 
 | |
| 	// plugin (pName) can be located in yaegi or wasm middleware builders.
 | |
| 	if descriptor, ok := b.middlewareBuilders[pName]; ok {
 | |
| 		m, err := descriptor.newMiddleware(config, middlewareName)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		return m.NewHandler, nil
 | |
| 	}
 | |
| 
 | |
| 	return nil, fmt.Errorf("unknown plugin type: %s", pName)
 | |
| }
 | |
| 
 | |
| func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest, moduleName string, settings Settings) (middlewareBuilder, error) {
 | |
| 	switch manifest.Runtime {
 | |
| 	case runtimeWasm:
 | |
| 		wasmPath, err := getWasmPath(manifest)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("wasm path: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		return newWasmMiddlewareBuilder(goPath, moduleName, wasmPath, settings)
 | |
| 
 | |
| 	case runtimeYaegi, "":
 | |
| 		i, err := newInterpreter(ctx, goPath, manifest, settings)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("failed to create Yaegi interpreter: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		return newYaegiMiddlewareBuilder(i, manifest.BasePkg, manifest.Import)
 | |
| 
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("unknown plugin runtime: %s", manifest.Runtime)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newProviderBuilder(ctx context.Context, manifest *Manifest, goPath string, settings Settings) (providerBuilder, error) {
 | |
| 	switch manifest.Runtime {
 | |
| 	case runtimeYaegi, "":
 | |
| 		i, err := newInterpreter(ctx, goPath, manifest, settings)
 | |
| 		if err != nil {
 | |
| 			return providerBuilder{}, err
 | |
| 		}
 | |
| 
 | |
| 		return providerBuilder{
 | |
| 			interpreter: i,
 | |
| 			Import:      manifest.Import,
 | |
| 			BasePkg:     manifest.BasePkg,
 | |
| 		}, nil
 | |
| 
 | |
| 	default:
 | |
| 		return providerBuilder{}, fmt.Errorf("unknown plugin runtime: %s", manifest.Runtime)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getWasmPath(manifest *Manifest) (string, error) {
 | |
| 	wasmPath := manifest.WasmPath
 | |
| 	if wasmPath == "" {
 | |
| 		wasmPath = "plugin.wasm"
 | |
| 	}
 | |
| 
 | |
| 	if !filepath.IsLocal(wasmPath) {
 | |
| 		return "", errors.New("wasmPath must be a local path")
 | |
| 	}
 | |
| 
 | |
| 	return wasmPath, nil
 | |
| }
 |