buildr/modules/plugin/module_help.go
Peter 34c431790e
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
refactor: use connect-go instead of regular Google gRPC
- support binary name for plugins
- register plugins for container jobs
2023-09-12 18:43:34 +02:00

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
}