Governor extension SDK is the SDK for developing Governor extensions. It provides a set of tools and utilities to help developers to create, test and deploy Governor extensions.
The validate
command validates ERD files in ./erds
Usage:
$ governor-extension-sdk erds validate
2023-11-08T17:44:35.527Z INFO cmd/erds.go:93 validating ERDs
2023-11-08T17:44:35.564Z INFO cmd/erds.go:140 ERDs are valid
Run governor-extension-sdk erds validate --help
for more information
The new
command creates a new ERD file populated with default place holder
values.
Usage:
$ governor-extension-sdk erds new --filename my-erd.yaml
2023-11-08T17:46:49.134Z INFO cmd/erds.go:222 creating new ERD my-erd.yaml
The default values can be overriden with additional flags:
Flags:
--description string description of the new ERD (default "some-description")
--enabled enabled status of the new ERD (default true)
--filename string filename of the new ERD, only .json, .yml and .yaml are supported
--name string name of the new ERD (default "hello-world")
--scope string scope of the new ERD (default "user")
--slug-plural string plural slug of the new ERD (default "greetings")
--slug-singular string singular slug of the new ERD (default "greeting")
--version string version of the new ERD (default "v1alpha1")
The event router listens to events from the Governor API and dispatches them to the appropriate event processors.
-
Create a new event processor that implements
pkg/eventprocessor.Processor
import ( "github.com/metal-toolbox/governor-extension-sdk/pkg/eventprocessor" "github.com/metal-toolbox/governor-api/pkg/api/v1alpha1" ) type Processor struct { // add fields here } // Processor implements the eventrouter.EventProcessor interface var _ eventprocessor.EventProcessor = (*Processor)(nil) func (p *Processor) ProcessEvent(ctx context.Context, event *event.Event) error { // add event processing logic here return nil } func (p. Processor) Register(router eventrouter.EventRouter, ext *v1alpha1.Extension) error { router.Create("groups", p.ProcessEvent) router.Update("groups", p.ProcessEvent) // ... add more event types here return nil }
-
Initialize a new event router
import ( "github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter" ) router := eventrouter.NewRouter()
-
Register the event processor with the event router
processor := &Processor{} // processor instance err := processor.Register(roueter, &v1alpha1.Extension{ /* extension metadata */ })
The server
package provides a simple HTTP server that listens to incoming
events from the Governor API.
With the event router initialized and event processor registered, the server can be created as follows:
import (
"github.com/metal-toolbox/governor-extension-sdk/pkg/server"
)
natsclient, err := server.NewNATSClient(/* nats client options */)
server := server.NewServer(
"0.0.0.0:8080",
"governor-extension-id",
"paht/to/erds",
server.WithEventRouter(router),
server.WithNATSClient(natsclient)
)
Developers can also provide only the event processor to the server, and the server will construct a new event router and register the event processor.
import (
"github.com/metal-toolbox/governor-extension-sdk/pkg/server"
)
server := server.NewServer(
"0.0.0.0:8080",
"governor-extension-id",
"paht/to/erds",
server.WithNATSClient(natsclient)
// multiple processors can be added here
server.WithEventProcessor(&Processor{}),
server.WithEventProcessor(/* another processor */),
server.WithEventProcessor(/* and another */),
)
Now the server can be started:
err := server.Run(ctx)
Tracing should work out of the box. Top level tracer is defined in server/server.go
and it can be passed to any event processors.
Event processors can inherit parent trace context in event.TraceContext
populated by the Governor API.
The trace context can be passed to other HTTP services by using the
WithTraceContext
roundtripper option.
import (
"net/http"
"github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter"
)
client := &http.Client{
Transport: roundtripper.NewGovExtRoundTripper(
http.DefaultTransport.RoundTrip,
roundtripper.WithTraceContext(),
),
}
To prevent infinite update loop that caused by the extension reacting to its own update, the event router provides a correlation ID processor that can be used to track the correlation ID of the incoming events, see this for more information.
The correlation ID processor works by extracting the correlation ID from the incoming event and storing it in the context with a middleware, it also checks if the correlation ID is already present in the context and if it is, it will skip the event processing. Finally, it will inject the correlation ID into the subsequent outgoing API requests to the Governor API.
For this to work, the event router should be initialized with the correlation ID processor:
import (
"github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter"
)
router := eventrouter.NewRouter(eventrouter.WithCorrelationIDProcessor(
eventrouter.NewCorrelationIDProcessor()
))
In addition, the http client used to make API requests to the Governor API should be initialized with the correlation ID Roundtripper:
import (
"net/http"
"github.com/metal-toolbox/governor-api/pkg/client"
"github.com/metal-toolbox/governor-extension-sdk/pkg/roundtripper"
)
client := governor.NewClient(
governor.WithHTTPClient(&http.Client{
Transport: roundtripper.NewGovExtRoundTripper(
http.DefaultTransport.RoundTrip,
roundtripper.WithCorrelationID(),
),
}),
/* other options */
)
The correlation ID processor provides three strategies to skip the event processing:
-
Skip All: Skip the any event processing if the correlation ID already exists
import ( "github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter" ) cidp := eventrouter.NewCorrelationIDProcessor( eventrouter.CorrelationIDProcessorWithSkipStrategySkipAll(), )
-
Update Only: Skip the event processing if the correlation ID already exists and the event type is
update
import ( "github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter" ) cidp := eventrouter.NewCorrelationIDProcessor( eventrouter.CorrelationIDProcessorWithSkipStrategyUpdateOnly(), )
-
Custom: Skip the event processing if the correlation ID already exists and the routes are provided in the custom skip strategy
import ( "github.com/metal-toolbox/governor-extension-sdk/pkg/eventrouter" "github.com/metal-toolbox/governor-api/pkg/events/v1alpha1" ) cidp := eventrouter.NewCorrelationIDProcessor( eventrouter.CorrelationIDProcessorWithSkipStrategyCustom(map[string]map[string]struct{}{ // skips all updates govevents.GovernorEventUpdate: {"*": {}}, // skips create groups and create roles govevents.GovernorEventCreate: { "groups": {}, "roles": {}, }, }), )