172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"connectrpc.com/connect"
|
|
"github.com/tetratelabs/wazero"
|
|
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
|
|
|
|
commonv1 "code.icb4dc0.de/buildr/api/generated/common/v1"
|
|
wasiv1 "code.icb4dc0.de/buildr/api/generated/wasi/v1"
|
|
"code.icb4dc0.de/buildr/api/generated/wasi/v1/rpcv1connect"
|
|
"code.icb4dc0.de/buildr/common/wasirpc"
|
|
|
|
"code.icb4dc0.de/buildr/buildr/modules"
|
|
)
|
|
|
|
func (m Module) Help(ctx context.Context) (help modules.Help, err error) {
|
|
runtime := m.prepareWASIRuntime(ctx)
|
|
|
|
defer func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
_ = runtime.Close(ctx)
|
|
}()
|
|
|
|
_, err = runtime.NewHostModuleBuilder("buildr").
|
|
NewFunctionBuilder().WithFunc(dummyWithResult).Export("log_msg").
|
|
NewFunctionBuilder().WithFunc(dummyWithResult).Export("get_state").
|
|
NewFunctionBuilder().WithFunc(dummyWithResult).Export("set_state").
|
|
NewFunctionBuilder().WithFunc(dummyWithResult).Export("exec").
|
|
NewFunctionBuilder().WithFunc(dummyWithResult).Export("lookPath").
|
|
Instantiate(ctx)
|
|
|
|
if err != nil {
|
|
return modules.Help{}, err
|
|
}
|
|
|
|
closer, err := wasi_snapshot_preview1.Instantiate(ctx, runtime)
|
|
if err != nil {
|
|
return modules.Help{}, err
|
|
}
|
|
|
|
defer func() {
|
|
if closeErr := closer.Close(context.Background()); closeErr != nil {
|
|
err = errors.Join(err, fmt.Errorf("failed to close WASI runtime: %w", closeErr))
|
|
}
|
|
}()
|
|
|
|
moduleConfig := wazero.NewModuleConfig().
|
|
WithStdout(io.Discard).
|
|
WithStderr(io.Discard)
|
|
|
|
pluginPayload, err := m.PluginPayload.Bytes()
|
|
if err != nil {
|
|
return modules.Help{}, err
|
|
}
|
|
|
|
mod, err := runtime.InstantiateWithConfig(ctx, pluginPayload, moduleConfig)
|
|
if err != nil {
|
|
return modules.Help{}, err
|
|
}
|
|
|
|
moduleClient, err := wasirpc.NewModuleClient(mod)
|
|
if err != nil {
|
|
return modules.Help{}, fmt.Errorf("failed to create module client: %w", err)
|
|
}
|
|
|
|
wasiClient := rpcv1connect.NewWasiExecutorServiceClient(moduleClient, fmt.Sprintf("wasi://%s", m.PluginType))
|
|
|
|
helpRequest := &wasiv1.HelpRequest{
|
|
ModuleReference: &commonv1.ModuleReference{
|
|
ModuleCategory: m.PluginCategory,
|
|
ModuleType: m.PluginType,
|
|
},
|
|
}
|
|
|
|
resp, err := wasiClient.Help(ctx, connect.NewRequest(helpRequest))
|
|
if err != nil {
|
|
return modules.Help{}, err
|
|
}
|
|
|
|
moduleHelp := modules.Help{
|
|
Name: resp.Msg.Name,
|
|
Description: resp.Msg.Description,
|
|
Examples: m.mapToExamples(resp.Msg.Examples),
|
|
}
|
|
|
|
return moduleHelp, nil
|
|
}
|
|
|
|
func (m Module) mapToExamples(raw []*wasiv1.TaskExample) []modules.Example {
|
|
examples := make([]modules.Example, 0, len(raw))
|
|
|
|
for _, example := range raw {
|
|
spec := &modules.Metadata[Module]{
|
|
Module: Module{
|
|
PluginCategory: m.Category(),
|
|
PluginType: m.Type(),
|
|
PluginPayload: m.PluginPayload,
|
|
modSpec: example.TaskSpec.ModuleSpec,
|
|
},
|
|
ModuleName: example.TaskSpec.ModuleName,
|
|
OutputDir: example.TaskSpec.OutputDir,
|
|
}
|
|
|
|
if example.TaskSpec.Container != nil {
|
|
spec.Container = &modules.ContainerSpec{
|
|
Image: example.TaskSpec.Container.Image,
|
|
User: example.TaskSpec.Container.User,
|
|
BindMounts: nil,
|
|
Privileged: example.TaskSpec.Container.Privileged,
|
|
}
|
|
|
|
if example.TaskSpec.Container.Capabilities != nil {
|
|
spec.Container.Capabilities = &modules.ContainerCapabilities{
|
|
Add: example.TaskSpec.Container.Capabilities.Add,
|
|
Drop: example.TaskSpec.Container.Capabilities.Drop,
|
|
}
|
|
}
|
|
|
|
if example.TaskSpec.Container.VolumeMounts != nil {
|
|
spec.Container.VolumeMounts = make([]modules.ContainerVolumeMount, 0, len(example.TaskSpec.Container.VolumeMounts))
|
|
|
|
for _, v := range example.TaskSpec.Container.VolumeMounts {
|
|
spec.Container.VolumeMounts = append(spec.Container.VolumeMounts, modules.ContainerVolumeMount{
|
|
Target: v.Target,
|
|
Name: v.Name,
|
|
ReadOnly: v.ReadOnly,
|
|
NoCopy: v.NoCopy,
|
|
})
|
|
}
|
|
}
|
|
|
|
if example.TaskSpec.Container.TmpfsMounts != nil {
|
|
spec.Container.TmpfsMounts = make([]modules.ContainerTmpfsMount, 0, len(example.TaskSpec.Container.TmpfsMounts))
|
|
|
|
for _, v := range example.TaskSpec.Container.TmpfsMounts {
|
|
spec.Container.TmpfsMounts = append(spec.Container.TmpfsMounts, modules.ContainerTmpfsMount{
|
|
Target: v.Target,
|
|
ReadOnly: v.ReadOnly,
|
|
Size: v.Size,
|
|
})
|
|
}
|
|
}
|
|
|
|
if example.TaskSpec.Container.BindMounts != nil {
|
|
spec.Container.BindMounts = make([]modules.ContainerBindMount, 0, len(example.TaskSpec.Container.BindMounts))
|
|
|
|
for _, v := range example.TaskSpec.Container.BindMounts {
|
|
spec.Container.BindMounts = append(spec.Container.BindMounts, modules.ContainerBindMount{
|
|
Source: v.Source,
|
|
Target: v.Target,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
examples = append(examples, modules.Example{
|
|
Name: example.Name,
|
|
Description: example.Description,
|
|
Spec: spec,
|
|
})
|
|
}
|
|
|
|
return examples
|
|
}
|