diff --git a/.golangci.yml b/.golangci.yml index b43bbfcb6..09e70a78c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -146,6 +146,7 @@ linters-settings: - github.com/mailgun/multibuf - github.com/jaguilar/vt100 - github.com/cucumber/godog + - github.com/http-wasm/http-wasm-host-go testifylint: disable: - suite-dont-use-pkg diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 951bf43ed..c72bced81 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -217,11 +217,29 @@ Timeout defines how long to wait on an idle session before releasing the related Local plugins configuration. (Default: ```false```) `--experimental.localplugins..modulename`: -plugin's module name. +Plugin's module name. + +`--experimental.localplugins..settings`: +Plugin's settings (works only for wasm plugins). + +`--experimental.localplugins..settings.envs`: +Environment variables to forward to the wasm guest. + +`--experimental.localplugins..settings.mounts`: +Directory to mount to the wasm guest. `--experimental.plugins..modulename`: plugin's module name. +`--experimental.plugins..settings`: +Plugin's settings (works only for wasm plugins). + +`--experimental.plugins..settings.envs`: +Environment variables to forward to the wasm guest. + +`--experimental.plugins..settings.mounts`: +Directory to mount to the wasm guest. + `--experimental.plugins..version`: plugin's version. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 5f3327d82..2f20c7946 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -217,11 +217,29 @@ Timeout defines how long to wait on an idle session before releasing the related Local plugins configuration. (Default: ```false```) `TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__MODULENAME`: -plugin's module name. +Plugin's module name. + +`TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS`: +Plugin's settings (works only for wasm plugins). + +`TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS_ENVS`: +Environment variables to forward to the wasm guest. + +`TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS_MOUNTS`: +Directory to mount to the wasm guest. `TRAEFIK_EXPERIMENTAL_PLUGINS__MODULENAME`: plugin's module name. +`TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS`: +Plugin's settings (works only for wasm plugins). + +`TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS_ENVS`: +Environment variables to forward to the wasm guest. + +`TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS_MOUNTS`: +Directory to mount to the wasm guest. + `TRAEFIK_EXPERIMENTAL_PLUGINS__VERSION`: plugin's version. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index c3ee80b19..b173f8eba 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -473,14 +473,26 @@ [experimental.plugins.Descriptor0] moduleName = "foobar" version = "foobar" + [experimental.plugins.Descriptor0.settings] + envs = ["foobar", "foobar"] + mounts = ["foobar", "foobar"] [experimental.plugins.Descriptor1] moduleName = "foobar" version = "foobar" + [experimental.plugins.Descriptor1.settings] + envs = ["foobar", "foobar"] + mounts = ["foobar", "foobar"] [experimental.localPlugins] [experimental.localPlugins.LocalDescriptor0] moduleName = "foobar" + [experimental.localPlugins.LocalDescriptor0.settings] + envs = ["foobar", "foobar"] + mounts = ["foobar", "foobar"] [experimental.localPlugins.LocalDescriptor1] moduleName = "foobar" + [experimental.localPlugins.LocalDescriptor1.settings] + envs = ["foobar", "foobar"] + mounts = ["foobar", "foobar"] [core] defaultRuleSyntax = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index f96e50f99..bc9343655 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -512,14 +512,42 @@ experimental: Descriptor0: moduleName: foobar version: foobar + settings: + envs: + - foobar + - foobar + mounts: + - foobar + - foobar Descriptor1: moduleName: foobar version: foobar + settings: + envs: + - foobar + - foobar + mounts: + - foobar + - foobar localPlugins: LocalDescriptor0: moduleName: foobar + settings: + envs: + - foobar + - foobar + mounts: + - foobar + - foobar LocalDescriptor1: moduleName: foobar + settings: + envs: + - foobar + - foobar + mounts: + - foobar + - foobar kubernetesGateway: true core: defaultRuleSyntax: foobar diff --git a/go.mod b/go.mod index 8a2f2fded..9b823e97d 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/http-wasm/http-wasm-host-go v0.6.0 github.com/influxdata/influxdb-client-go/v2 v2.7.0 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d + github.com/juliens/wasm-goexport v0.0.6 github.com/klauspost/compress v1.17.2 github.com/kvtools/consul v1.0.2 github.com/kvtools/etcdv3 v1.0.2 @@ -53,12 +54,14 @@ require ( github.com/rs/zerolog v1.29.0 github.com/sirupsen/logrus v1.9.3 github.com/spiffe/go-spiffe/v2 v2.1.1 + github.com/stealthrocket/wasi-go v0.8.0 + github.com/stealthrocket/wazergo v0.19.1 github.com/stretchr/testify v1.9.0 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 github.com/testcontainers/testcontainers-go v0.30.0 github.com/testcontainers/testcontainers-go/modules/k3s v0.30.0 - github.com/tetratelabs/wazero v1.5.0 + github.com/tetratelabs/wazero v1.7.2 github.com/tidwall/gjson v1.17.0 github.com/traefik/grpc-web v0.16.0 github.com/traefik/paerser v0.2.0 @@ -371,3 +374,5 @@ exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible // https://github.com/docker/compose/blob/v2.19.0/go.mod#L12 replace github.com/cucumber/godog => github.com/cucumber/godog v0.13.0 + +replace github.com/http-wasm/http-wasm-host-go => github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70 diff --git a/go.sum b/go.sum index 80cb51429..0112e07e3 100644 --- a/go.sum +++ b/go.sum @@ -592,8 +592,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/http-wasm/http-wasm-host-go v0.6.0 h1:Vd4XvcFB3NMgWp2VLCQaiqYgLneN2lChbyN9NGoNDro= -github.com/http-wasm/http-wasm-host-go v0.6.0/go.mod h1:zQB3w+df4hryDEqBorGyA1DwPJ86LfKIASNLFuj6CuI= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -635,6 +633,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juliens/wasm-goexport v0.0.6 h1:YU0c+j0dF/HNy32vgYTA+K/6wnsZXgGc+ihl/UDw8iA= +github.com/juliens/wasm-goexport v0.0.6/go.mod h1:VTTpJVY3tIBet0Gv8r5TxdsNg0vDkkqXYm0Hp5hR42A= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= @@ -1051,6 +1051,10 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spiffe/go-spiffe/v2 v2.1.1 h1:RT9kM8MZLZIsPTH+HKQEP5yaAk3yd/VBzlINaRjXs8k= github.com/spiffe/go-spiffe/v2 v2.1.1/go.mod h1:5qg6rpqlwIub0JAiF1UK9IMD6BpPTmvG6yfSgDBs5lg= +github.com/stealthrocket/wasi-go v0.8.0 h1:Hwnv3CUoMhhRyero9vt1vfwaYa9tu/Z5kmCW4WeAmVI= +github.com/stealthrocket/wasi-go v0.8.0/go.mod h1:PJ5oVs2E1ciOJnsTnav4nvTtEcJ4D1jUZAewS9pzuZg= +github.com/stealthrocket/wazergo v0.19.1 h1:BPrITETPgSFwiytwmToO0MbUC/+RGC39JScz1JmmG6c= +github.com/stealthrocket/wazergo v0.19.1/go.mod h1:riI0hxw4ndZA5e6z7PesHg2BtTftcZaMxRcoiGGipTs= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -1086,8 +1090,8 @@ github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlCl github.com/testcontainers/testcontainers-go v0.30.0/go.mod h1:K+kHNGiM5zjklKjgTtcrEetF3uhWbMUyqAQoyoh8Pf0= github.com/testcontainers/testcontainers-go/modules/k3s v0.30.0 h1:Mk47J0WcLoY2ig72lPl+/w8GTPYbRCdHoWcPjV2mVr8= github.com/testcontainers/testcontainers-go/modules/k3s v0.30.0/go.mod h1:CNnA3717kbp5wRxz+gU/cAwX6+4+OOispIsjHmKsEWQ= -github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= -github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= +github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc= +github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y= github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -1103,6 +1107,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY= github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4= +github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70 h1:I+oBnV0orhmasb87yaX54tOAfqrV9+yKoQ1Cum5mq8w= +github.com/traefik/http-wasm-host-go v0.0.0-20240618100324-3c53dcaa1a70/go.mod h1:zQB3w+df4hryDEqBorGyA1DwPJ86LfKIASNLFuj6CuI= github.com/traefik/paerser v0.2.0 h1:zqCLGSXoNlcBd+mzqSCLjon/I6phqIjeJL2xFB2ysgQ= github.com/traefik/paerser v0.2.0/go.mod h1:afzaVcgF8A+MpTnPG4wBr4whjanCSYA6vK5RwaYVtRc= github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E= diff --git a/pkg/plugins/builder.go b/pkg/plugins/builder.go index 17ba1022b..9e47d134b 100644 --- a/pkg/plugins/builder.go +++ b/pkg/plugins/builder.go @@ -52,7 +52,7 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[ switch manifest.Type { case typeMiddleware: - middleware, err := newMiddlewareBuilder(logCtx, client.GoPath(), manifest, desc.ModuleName) + middleware, err := newMiddlewareBuilder(logCtx, client.GoPath(), manifest, desc.ModuleName, desc.Settings) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[ switch manifest.Type { case typeMiddleware: - middleware, err := newMiddlewareBuilder(logCtx, localGoPath, manifest, desc.ModuleName) + middleware, err := newMiddlewareBuilder(logCtx, localGoPath, manifest, desc.ModuleName, desc.Settings) if err != nil { return nil, err } @@ -128,7 +128,7 @@ func (b Builder) Build(pName string, config map[string]interface{}, middlewareNa return nil, fmt.Errorf("unknown plugin type: %s", pName) } -func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest, moduleName string) (middlewareBuilder, error) { +func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest, moduleName string, settings Settings) (middlewareBuilder, error) { switch manifest.Runtime { case runtimeWasm: wasmPath, err := getWasmPath(manifest) @@ -136,7 +136,7 @@ func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest return nil, fmt.Errorf("wasm path: %w", err) } - return newWasmMiddlewareBuilder(goPath, moduleName, wasmPath), nil + return newWasmMiddlewareBuilder(goPath, moduleName, wasmPath, settings) case runtimeYaegi, "": i, err := newInterpreter(ctx, goPath, manifest.Import) diff --git a/pkg/plugins/middlewarewasm.go b/pkg/plugins/middlewarewasm.go index b307850d9..a9f4f5ada 100644 --- a/pkg/plugins/middlewarewasm.go +++ b/pkg/plugins/middlewarewasm.go @@ -8,20 +8,38 @@ import ( "os" "path/filepath" "reflect" + "strings" "github.com/http-wasm/http-wasm-host-go/handler" wasm "github.com/http-wasm/http-wasm-host-go/handler/nethttp" + "github.com/juliens/wasm-goexport/host" "github.com/tetratelabs/wazero" "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" ) type wasmMiddlewareBuilder struct { - path string + path string + cache wazero.CompilationCache + settings Settings } -func newWasmMiddlewareBuilder(goPath string, moduleName, wasmPath string) *wasmMiddlewareBuilder { - return &wasmMiddlewareBuilder{path: filepath.Join(goPath, "src", moduleName, wasmPath)} +func newWasmMiddlewareBuilder(goPath, moduleName, wasmPath string, settings Settings) (*wasmMiddlewareBuilder, error) { + ctx := context.Background() + path := filepath.Join(goPath, "src", moduleName, wasmPath) + cache := wazero.NewCompilationCache() + + code, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("loading Wasm binary: %w", err) + } + + rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().WithCompilationCache(cache)) + if _, err = rt.CompileModule(ctx, code); err != nil { + return nil, fmt.Errorf("compiling guest module: %w", err) + } + + return &wasmMiddlewareBuilder{path: path, cache: cache, settings: settings}, nil } func (b wasmMiddlewareBuilder) newMiddleware(config map[string]interface{}, middlewareName string) (pluginMiddleware, error) { @@ -33,15 +51,64 @@ func (b wasmMiddlewareBuilder) newMiddleware(config map[string]interface{}, midd } func (b wasmMiddlewareBuilder) newHandler(ctx context.Context, next http.Handler, cfg reflect.Value, middlewareName string) (http.Handler, error) { + h, applyCtx, err := b.buildMiddleware(ctx, next, cfg, middlewareName) + if err != nil { + return nil, fmt.Errorf("building Wasm middleware: %w", err) + } + + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + h.ServeHTTP(rw, req.WithContext(applyCtx(req.Context()))) + }), nil +} + +func (b *wasmMiddlewareBuilder) buildMiddleware(ctx context.Context, next http.Handler, cfg reflect.Value, middlewareName string) (http.Handler, func(ctx context.Context) context.Context, error) { code, err := os.ReadFile(b.path) if err != nil { - return nil, fmt.Errorf("loading Wasm binary: %w", err) + return nil, nil, fmt.Errorf("loading binary: %w", err) + } + + rt := host.NewRuntime(wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().WithCompilationCache(b.cache))) + + guestModule, err := rt.CompileModule(ctx, code) + if err != nil { + return nil, nil, fmt.Errorf("compiling guest module: %w", err) + } + + applyCtx, err := InstantiateHost(ctx, rt, guestModule, b.settings) + if err != nil { + return nil, nil, fmt.Errorf("instantiating host module: %w", err) } logger := middlewares.GetLogger(ctx, middlewareName, "wasm") + config := wazero.NewModuleConfig().WithSysWalltime() + for _, env := range b.settings.Envs { + config.WithEnv(env, os.Getenv(env)) + } + + if len(b.settings.Mounts) > 0 { + fsConfig := wazero.NewFSConfig() + for _, mount := range b.settings.Mounts { + withDir := fsConfig.WithDirMount + prefix, readOnly := strings.CutSuffix(mount, ":ro") + if readOnly { + withDir = fsConfig.WithReadOnlyDirMount + } + parts := strings.Split(prefix, ":") + switch { + case len(parts) == 1: + withDir(parts[0], parts[0]) + case len(parts) == 2: + withDir(parts[0], parts[1]) + default: + return nil, nil, fmt.Errorf("invalid directory %q", mount) + } + } + config.WithFSConfig(fsConfig) + } + opts := []handler.Option{ - handler.ModuleConfig(wazero.NewModuleConfig().WithSysWalltime()), + handler.ModuleConfig(config), handler.Logger(logs.NewWasmLogger(logger)), } @@ -49,23 +116,27 @@ func (b wasmMiddlewareBuilder) newHandler(ctx context.Context, next http.Handler if i != nil { config, ok := i.(map[string]interface{}) if !ok { - return nil, fmt.Errorf("could not type assert config: %T", i) + return nil, nil, fmt.Errorf("could not type assert config: %T", i) } data, err := json.Marshal(config) if err != nil { - return nil, fmt.Errorf("marshaling config: %w", err) + return nil, nil, fmt.Errorf("marshaling config: %w", err) } opts = append(opts, handler.GuestConfig(data)) } - mw, err := wasm.NewMiddleware(context.Background(), code, opts...) + opts = append(opts, handler.Runtime(func(ctx context.Context) (wazero.Runtime, error) { + return rt, nil + })) + + mw, err := wasm.NewMiddleware(applyCtx(ctx), code, opts...) if err != nil { - return nil, err + return nil, nil, fmt.Errorf("creating middleware: %w", err) } - return mw.NewHandler(ctx, next), nil + return mw.NewHandler(ctx, next), applyCtx, nil } // WasmMiddleware is an HTTP handler plugin wrapper. diff --git a/pkg/plugins/types.go b/pkg/plugins/types.go index 35b357faf..23254f7f7 100644 --- a/pkg/plugins/types.go +++ b/pkg/plugins/types.go @@ -10,6 +10,11 @@ const ( typeProvider = "provider" ) +type Settings struct { + Envs []string `description:"Environment variables to forward to the wasm guest." json:"envs,omitempty" toml:"envs,omitempty" yaml:"envs,omitempty"` + Mounts []string `description:"Directory to mount to the wasm guest." json:"mounts,omitempty" toml:"mounts,omitempty" yaml:"mounts,omitempty"` +} + // Descriptor The static part of a plugin configuration. type Descriptor struct { // ModuleName (required) @@ -17,12 +22,18 @@ type Descriptor struct { // Version (required) Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"` + + // Settings (optional) + Settings Settings `description:"Plugin's settings (works only for wasm plugins)." json:"settings,omitempty" toml:"settings,omitempty" yaml:"settings,omitempty" export:"true"` } // LocalDescriptor The static part of a local plugin configuration. type LocalDescriptor struct { // 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"` + + // Settings (optional) + Settings Settings `description:"Plugin's settings (works only for wasm plugins)." json:"settings,omitempty" toml:"settings,omitempty" yaml:"settings,omitempty" export:"true"` } // Manifest The plugin manifest. diff --git a/pkg/plugins/wasip.go b/pkg/plugins/wasip.go new file mode 100644 index 000000000..6e2b14793 --- /dev/null +++ b/pkg/plugins/wasip.go @@ -0,0 +1,59 @@ +//go:build !windows + +package plugins + +import ( + "context" + "fmt" + "os" + + "github.com/stealthrocket/wasi-go/imports" + wazergo_wasip1 "github.com/stealthrocket/wasi-go/imports/wasi_snapshot_preview1" + "github.com/stealthrocket/wazergo" + "github.com/tetratelabs/wazero" + wazero_wasip1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +type ContextApplier func(ctx context.Context) context.Context + +// InstantiateHost instantiates the Host module according to the guest requirements (for now only SocketExtensions). +func InstantiateHost(ctx context.Context, runtime wazero.Runtime, mod wazero.CompiledModule, settings Settings) (ContextApplier, error) { + if extension := imports.DetectSocketsExtension(mod); extension != nil { + envs := []string{} + for _, env := range settings.Envs { + envs = append(envs, fmt.Sprintf("%s=%s", env, os.Getenv(env))) + } + + builder := imports.NewBuilder().WithSocketsExtension("auto", mod) + if len(envs) > 0 { + builder.WithEnv(envs...) + } + + if len(settings.Mounts) > 0 { + builder.WithDirs(settings.Mounts...) + } + + ctx, sys, err := builder.Instantiate(ctx, runtime) + if err != nil { + return nil, err + } + + inst, err := wazergo.Instantiate(ctx, runtime, wazergo_wasip1.NewHostModule(*extension), wazergo_wasip1.WithWASI(sys)) + if err != nil { + return nil, fmt.Errorf("wazergo instantiation: %w", err) + } + + return func(ctx context.Context) context.Context { + return wazergo.WithModuleInstance(ctx, inst) + }, nil + } + + _, err := wazero_wasip1.Instantiate(ctx, runtime) + if err != nil { + return nil, fmt.Errorf("wazero instantiation: %w", err) + } + + return func(ctx context.Context) context.Context { + return ctx + }, nil +} diff --git a/pkg/plugins/wasip_windows.go b/pkg/plugins/wasip_windows.go new file mode 100644 index 000000000..9cdd473e7 --- /dev/null +++ b/pkg/plugins/wasip_windows.go @@ -0,0 +1,18 @@ +//go:build windows + +package plugins + +import ( + "context" + + "github.com/tetratelabs/wazero" +) + +type ContextApplier func(ctx context.Context) context.Context + +// InstantiateHost instantiates the Host module. +func InstantiateHost(ctx context.Context, runtime wazero.Runtime, mod wazero.CompiledModule, settings Settings) (ContextApplier, error) { + return func(ctx context.Context) context.Context { + return ctx + }, nil +} diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go index ee653b28a..592d91cfd 100644 --- a/pkg/redactor/redactor_config_test.go +++ b/pkg/redactor/redactor_config_test.go @@ -907,18 +907,34 @@ func TestDo_staticConfiguration(t *testing.T) { "Descriptor0": { ModuleName: "foobar", Version: "foobar", + Settings: plugins.Settings{ + Envs: []string{"a", "b"}, + Mounts: []string{"a", "b"}, + }, }, "Descriptor1": { ModuleName: "foobar", Version: "foobar", + Settings: plugins.Settings{ + Envs: []string{"a", "b"}, + Mounts: []string{"a", "b"}, + }, }, }, LocalPlugins: map[string]plugins.LocalDescriptor{ "Descriptor0": { ModuleName: "foobar", + Settings: plugins.Settings{ + Envs: []string{"a", "b"}, + Mounts: []string{"a", "b"}, + }, }, "Descriptor1": { ModuleName: "foobar", + Settings: plugins.Settings{ + Envs: []string{"a", "b"}, + Mounts: []string{"a", "b"}, + }, }, }, } diff --git a/pkg/redactor/testdata/anonymized-static-config.json b/pkg/redactor/testdata/anonymized-static-config.json index a1b7b19ec..1dbd65fd7 100644 --- a/pkg/redactor/testdata/anonymized-static-config.json +++ b/pkg/redactor/testdata/anonymized-static-config.json @@ -388,19 +388,59 @@ "plugins": { "Descriptor0": { "moduleName": "foobar", - "version": "foobar" + "version": "foobar", + "settings": { + "envs": [ + "xxxx", + "xxxx" + ], + "mounts": [ + "xxxx", + "xxxx" + ] + } }, "Descriptor1": { "moduleName": "foobar", - "version": "foobar" + "version": "foobar", + "settings": { + "envs": [ + "xxxx", + "xxxx" + ], + "mounts": [ + "xxxx", + "xxxx" + ] + } } }, "localPlugins": { "Descriptor0": { - "moduleName": "foobar" + "moduleName": "foobar", + "settings": { + "envs": [ + "xxxx", + "xxxx" + ], + "mounts": [ + "xxxx", + "xxxx" + ] + } }, "Descriptor1": { - "moduleName": "foobar" + "moduleName": "foobar", + "settings": { + "envs": [ + "xxxx", + "xxxx" + ], + "mounts": [ + "xxxx", + "xxxx" + ] + } } } }