Go plugin Caveats

Go plugin Caveats

A Go plugin is a compilation artifact described by the Go plugin package. It is built with special flags and cannot run on its own. It must be loaded into a running Go program.

A normal program written in Go might be usable as exec plugin, but is not a Go plugin.

Go plugins allow kustomize extensions that run without the cost marshalling/unmarshalling all resource data to/from a subprocess for each plugin run. The Go plugin API assures a certain level of consistency to avoid confusing downstream transformers.

Go plugins work as described in the plugin package, but fall short of common notions associated with the word plugin.

The skew problem

Go plugin compilation creates an ELF formatted .so file, which by definition has no information about the provenance of the object code.

Skew between the compilation conditions (versions of package dependencies, GOOS, GOARCH) of the main program ELF and the plugin ELF will cause plugin load failure, with non-helpful error messages.

Exec plugins also lack provenance, but won’t fail due to compilation skew.

In either case, the only sensible way to share a plugin is as some kind of bundle (a git repo URL, a git archive file, a tar file, etc.) containing source code, tests and associated data, unpackable under $XDG_CONFIG_HOME/kustomize/plugin.

In the case of a Go plugin, an end user accepting a shared plugin must compile both kustomize and the plugin.

This means a one-time run of

  1. # Or whatever is appropriate at time of reading
  2. GOPATH=${whatever} GO111MODULE=on go get sigs.k8s.io/kustomize/api

and then a normal development cycle using

  1. go build -buildmode plugin \
  2. -o ${wherever}/${kind}.so ${wherever}/${kind}.go

with paths and the release version tag (e.g. v3.0.0) adjusted as needed.

For comparison, consider what one must do to write a tensorflow plugin.

Why support Go plugins

Safety

The Go plugin developer sees the same API offered to native kustomize operations, assuring certain semantics, invariants, checks, etc. An exec plugin sub-process dealing with this via stdin/stdout will have an easier time screwing things up for downstream transformers and consumers.

Minor point: if the plugin reads files via the kustomize-provided file Loader interface, it will be constrained by kustomize file loading restrictions. Of course, nothing but a code audit prevents a Go plugin from importing the io package and doing whatever it wants.

Debugging

A Go plugin developer can debug the plugin in situ, setting breakpoints inside the plugin and elsewhere while running a plugin in feature tests.

To get the best of both worlds (shareability and safety), a developer can write an .go program that functions as an exec plugin, but can be processed by go generate to emit a Go plugin (or vice versa).

Unit of contribution

All the builtin generators and transformers are themselves Go plugins. This means that the kustomize maintainers can promote a contributed plugin to a builtin without needing code changes (beyond those mandated by normal code review).

Ecosystems grow through use

Tooling could ease Go plugin sharing, but this requires some critical mass of Go plugin authoring, which in turn is hampered by confusion around sharing. Go modules, once they are more widely adopted, will solve the biggest plugin sharing difficulty: ambiguous plugin vs host dependencies.