Skip to content

Writing a plugin

Writing a plugin consists of several steps, each described below.

apiVersion: fundament.io/v1
kind: PluginDefinition
spec:
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: Failed
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})
}
FROM golang:1.26-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
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-plugin
COPY plugins/my-plugin/definition.yaml /app/definition.yaml
WORKDIR /app
ENTRYPOINT ["/my-plugin"]
apiVersion: plugins.fundament.io/v1
kind: PluginInstallation
metadata:
name: my-plugin
namespace: fundament
spec:
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-admin

Every plugin exposes a ConnectRPC service that the controller and console consume:

service PluginMetadataService {
rpc GetStatus(GetStatusRequest) returns (GetStatusResponse);
rpc GetDefinition(GetDefinitionRequest) returns (GetDefinitionResponse);
}
ConsumerMethodPurpose
Plugin ControllerGetStatusPoll phase, message, version → write to CR .status
Console FrontendGetDefinitionFetch menu entries, UI hints, CRDs → render plugin UI

A self-contained development environment for plugin development. See plugins/README.md for setup instructions and available commands.