Writing a plugin
Writing a plugin consists of several steps, each described below.
1. Create definition.yaml
Section titled “1. Create definition.yaml”apiVersion: fundament.io/v1kind: PluginDefinitionspec: metadata: name: my-plugin displayName: My Plugin version: v1.0.0 description: Does something useful author: My Team license: Apache-2.0 icon: puzzle tags: - example
permissions: capabilities: - internet_access rbac: - apiGroups: ["my-api.io"] resources: ["myresources"] verbs: ["get", "list", "watch"]
menu: project: - crd: myresources.my-api.io list: true detail: true create: true icon: box
uiHints: myresources.my-api.io: statusMapping: jsonPath: ".status.phase" values: "Ready": badge: success label: Ready "Failed": badge: danger label: Failed2. Implement the plugin
Section titled “2. Implement the plugin”package main
import ( "context" "fmt" "log"
"github.com/fundament-oss/fundament/plugin-sdk/pluginruntime")
type MyPlugin struct { def pluginruntime.PluginDefinition}
func (p *MyPlugin) Definition() pluginruntime.PluginDefinition { return p.def}
func (p *MyPlugin) Start(ctx context.Context, host pluginruntime.Host) error { host.ReportStatus(pluginruntime.PluginStatus{ Phase: pluginruntime.PhaseInstalling, Message: "setting up", })
// Do setup work...
host.ReportReady() host.ReportStatus(pluginruntime.PluginStatus{ Phase: pluginruntime.PhaseRunning, Message: "operational", })
<-ctx.Done() return nil}
func (p *MyPlugin) Shutdown(_ context.Context) error { return nil}
func main() { def, err := pluginruntime.LoadDefinition("definition.yaml") if err != nil { log.Fatal(err) } pluginruntime.Run(&MyPlugin{def: def})}3. Build a container image
Section titled “3. Build a container image”FROM golang:1.26-alpine AS builderWORKDIR /buildCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 go build -o /bin/my-plugin ./plugins/my-plugin
FROM alpine:3.21# Add any CLI tools your plugin needs (e.g. helm)COPY --from=builder /bin/my-plugin /my-pluginCOPY plugins/my-plugin/definition.yaml /app/definition.yamlWORKDIR /appENTRYPOINT ["/my-plugin"]4. Create a PluginInstallation
Section titled “4. Create a PluginInstallation”apiVersion: plugins.fundament.io/v1kind: PluginInstallationmetadata: name: my-plugin namespace: fundamentspec: image: registry.example.com/my-plugin:v1.0.0 pluginName: my-plugin version: v1.0.0 # Only if your plugin needs cluster-wide access: # clusterRoles: # - cluster-adminMetadata API
Section titled “Metadata API”Every plugin exposes a ConnectRPC service that the controller and console consume:
service PluginMetadataService { rpc GetStatus(GetStatusRequest) returns (GetStatusResponse); rpc GetDefinition(GetDefinitionRequest) returns (GetDefinitionResponse);}| Consumer | Method | Purpose |
|---|---|---|
| Plugin Controller | GetStatus | Poll phase, message, version → write to CR .status |
| Console Frontend | GetDefinition | Fetch menu entries, UI hints, CRDs → render plugin UI |
Plugin sandbox
Section titled “Plugin sandbox”A self-contained development environment for plugin development. See plugins/README.md for setup instructions and available commands.