goplugin

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2026 License: MIT Imports: 17 Imported by: 0

README

Go Plugin System

go-plugin is a go plugin system that combined two most popular golang plugin systems: hashicorp/go-plugin and knqyf263/go-plugin, providing consistent between gRPC and Wasm plugin experience.

Architecture

Plugin

Plugin is packs into a single plg file, essentially a zip file.

Say there is a greeter plugin, the structure is as shown in below

greeter.plg
├── info.yaml
└── Content/

Content is a directory containing all resources used by the plugin, including executable, wasm, and static resources.

info.yaml defines the plugin. It looks like the following.

# Required
Name: com.example/greeter
Version: 1.0.0
Type: grpc             # enum: grpc | wasm
ContractVersion: 1     # host check this for compatibility
Command: $PLUGIN_ROOT/greeter run
# Optional custom metadata
DisplayName: Greeter
Category: demo

In the case of wasm, field Command will be the location of wasm file

plg file will be extracted to a temporary location everytime before loading the plugin.

$PLUGIN_ROOT will be the location of Content

Host

Plugins ane host are communicated using protobuf3 protocol.

SDKs, or pb files will be generated from proto files. They define interfaces and data structures used to communicate.

Installation

This module uses knqyf263/go-plugin as backend for Wasm. To develop Wasm plugin, you need a compiler.

To develop a plugin, protobuf is required, see documentation for instructions.

And install go module by

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

Usage

For open box example, see basic example.

Generate interface

If you're not provided pb files (sdk), you need to generate it from proto file, where interfaces are defined.

After that, generate SDK

protoc \
-I. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
<my-plugin>.proto

Then import into host

import (
    pb "example.com/my-plugin/proto
)
Initialize manager

Handshake defines some information that will be checked prior to establishing a gRPC connection.

goplugin.HandshakeConfig{
    ProtocolVersion:  1,
    MagicCookieKey:   "GRPC_PLUGIN",
    MagicCookieValue: "hello",
}

Prepare for configs. Here binds services defined in proto

goplugin.GRPCConfig{
    HandshakeConfig: handshake,
    Loader: func(_ context.Context, c *grpc.ClientConn) (any, error) {
        return pb.NewPluginClient(c), nil
    },
},

Load the config

mgr, err := goplugin.NewManager(goplugin.Config{
    TempDir: "/tmp",
    GRPC: GRPCConfig,
    WASM: nil,
})
Use plugin

Load the plugin

handle, _ := mgr.Load("my-plugin.plg")
defer mgr.Unload(handle)
// pb.<PluginSDK> is a placeholder, definitions at .proto
client, _ := handle.Client().(pb.<PluginSDK>)

Call plugin methods

resp, _ := client.Hello(ctx, &pb.<Params>{To: "World"})
fmt.Printf("Hello %s", resp.<GetMessage>())

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClientConfig

type ClientConfig = hcplugin.ClientConfig

type Config

type Config struct {
	TempDir string

	GRPC *GRPCConfig
	WASM *WASMConfig
}

type GRPCConfig

type GRPCConfig struct {
	HandshakeConfig HandshakeConfig

	RunAsUser string

	AllowedProtocols []Protocol
	Stderr           io.Writer
	SyncStdout       io.Writer
	SyncStderr       io.Writer

	// Loader is used by the default gRPC preset.
	// If nil, the preset returns *grpc.ClientConn as client.
	Loader func(ctx context.Context, conn *grpc.ClientConn) (any, error)

	ClientConfigOverride func(*ClientConfig)
}

type Handle

type Handle struct {
	// contains filtered or unexported fields
}

func (*Handle) Client

func (h *Handle) Client() any

func (*Handle) Close

func (h *Handle) Close(ctx context.Context) error

func (*Handle) Info

func (h *Handle) Info() Info

func (*Handle) PluginPath

func (h *Handle) PluginPath() string

type HandshakeConfig

type HandshakeConfig struct {
	ProtocolVersion  int
	MagicCookieKey   string
	MagicCookieValue string
}

type Info

type Info struct {
	Name            string         `yaml:"Name"`
	Version         string         `yaml:"Version"`
	Type            string         `yaml:"Type"`
	ContractVersion int            `yaml:"ContractVersion"`
	Command         string         `yaml:"Command"`
	Metadata        map[string]any `yaml:",inline"`
}

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

func NewManager

func NewManager(cfg Config) (*Manager, error)

func (*Manager) Load

func (m *Manager) Load(path string) (*Handle, error)

func (*Manager) Unload

func (m *Manager) Unload(h *Handle) error

type Protocol

type Protocol string
const (
	ProtocolGRPC Protocol = Protocol(hcplugin.ProtocolGRPC)
)

type WASMConfig

type WASMConfig struct {
	// Loader receives resolved module path from info.Command and returns:
	// 1) plugin client instance used by caller
	// 2) cleanup function invoked on Unload
	Loader func(ctx context.Context, modulePath string, info Info) (client any, cleanup func(context.Context) error, err error)

	// Reserved for future parity with design doc.
	RuntimeConfigOverride any
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL