Add docs for writing go based plugins

This commit is contained in:
sophia 2022-07-12 11:57:52 -05:00
parent ad0a164871
commit b470522767
3 changed files with 309 additions and 0 deletions

View File

@ -0,0 +1,222 @@
---
layout: docs
page_title: Custom Guests - Go Plugin Development
description: |-
This page documents how to add new guest OS detection to Vagrant, allowing
Vagrant to properly configure new operating systems. Prior to reading this,
you should be familiar with the plugin development basics.
---
# Go Plugin Development: Guests
Outside of these components, the caller must provide all other arguments
that are required.
The most basic guest plugin is composed of:
1. A detection function that determines if the plugin is usable on the system
(is the expected guest)
2. A set of capabilities that define actions that can be run against the guest
3. An entry point defining the plugin options
**Note**: To quickly get started writing Go guest plugins, clone the [vagrant-guest-plugin-skeleton](https://github.com/soapy1/vagrant-guest-plugin-skeleton)
template and follow the Readme.
The file structure of a guest plugin looks like:
```
- myplugin
|- main.go
\- guest
|- myguest.go
\- cap
|- mycapability.go
```
Where `main.go` defines the plugin options. `guest/myguest.go` defines the core plugin
functionality including the detection of the guest. `cap/*` has the definitions of
all the guest plugin capabilities. These capabilities are the same as those for [Ruby
plugins](../guest-capabilities).
## Writing a guest plugin
A guest must satisfy the interface defined for a guest component
```
GuestDetectFunc() interface{}
ParentFunc() interface{}
HasCapabilityFunc() interface{}
CapabilityFunc(capName string) interface{}
```
Src: https://github.com/hashicorp/vagrant-plugin-sdk/blob/main/component/component.go
`GuestDetectFunc`: returns a function that defines the code that determines if the guest is
detected. The returned function must return a `bool`.
`ParentFunc`: returns a function that defines the code that determines the most immediate parent
plugin. A child plugin will inherit all the capabilities defined in the parent. The
returned function must return a `string`.
`HasCapabilityFunc`: returns a function that defines a lookup for a capability. The returned
function must return an `bool`.
`CapabilityFunc`: returns a capability function that is defined by the plugin registered by a given name.
An example guest plugin
```
// file: myplugin/guest/myguest.go
package guest
import (
"github.com/hashicorp/vagrant-plugin-sdk/component"
sdkcore "github.com/hashicorp/vagrant-plugin-sdk/core"
)
// AlwaysTrueGuest is a Guest implementation for myplugin.
type AlwaysTrueGuest struct {
}
// DetectFunc implements component.Guest
func (h *AlwaysTrueGuest) GuestDetectFunc() interface{} {
return h.Detect
}
func (h *AlwaysTrueGuest) Detect(t sdkcore.Target) (bool, error) {
return true, nil
}
// ParentsFunc implements component.Guest
func (h *AlwaysTrueGuest) ParentFunc() interface{} {
return h.Parent
}
func (h *AlwaysTrueGuest) Parent() string {
// This plugin has no parents
return ""
}
// HasCapabilityFunc implements component.Guest
func (h *AlwaysTrueGuest) HasCapabilityFunc() interface{} {
return h.CheckCapability
}
func (h *AlwaysTrueGuest) CheckCapability(n *component.NamedCapability) bool {
// This plugin has no capabilities
return false
}
// CapabilityFunc implements component.Guest
func (h *AlwaysTrueGuest) CapabilityFunc(name string) interface{} {
return fmt.Errorf("requested capability %s not found", name)
}
var (
_ component.Guest = (*AlwaysTrueGuest)(nil)
)
```
```
// file: myplugin/main.go
package myplugin
import (
sdk "github.com/hashicorp/vagrant-plugin-sdk"
"github.com/hashicorp/vagrant/builtin/myplugin/guest"
)
// Options are the SDK options to use for instantiation.
var ComponentOptions = []sdk.Option{
sdk.WithComponents(
// Include the defined guest as a component defined in this plugin
&guest.AlwaysTrueGuest{},
),
}
func main() {
sdk.Main(ComponentOptions...)
os.Exit(0)
}
```
In this example, the guest plugin will always be detected. It does not define any
capabilities, or have any parent plugins.
### Defining and registering guest capabilities
A guest plugin may have capabilities two ways:
1. By defining and implementing the capability in the plugin
2. By inheriting the capability from a parent guest plugin
Define a capability by writing out a function that returns the desired capability
```
// file: myplugin/guest/cap/mycapability.go
package cap
import (
"io/ioutil"
"github.com/hashicorp/vagrant-plugin-sdk/terminal"
)
func WriteHelloFunc() interface{} {
return WriteHello
}
func WriteHello(trm terminal.UI) error {
trm.Output("Hello world")
return nil
}
```
Make the capability available to the plugin by filling in the capability functions
```
myplugin/guest/myguest.go
// HasCapabilityFunc implements component.Guest
func (h *AlwaysTrueGuest) HasCapabilityFunc() interface{} {
return h.CheckCapability
}
func (h *AlwaysTrueGuest) CheckCapability(n *component.NamedCapability) bool {
if n.Capability == "write_hello" {
return true
}
return false
}
// CapabilityFunc implements component.Guest
func (h *AlwaysTrueGuest) CapabilityFunc(name string) interface{} {
if name == "write_hello" {
return h.WriteHelloCap
}
return errors.New("Invalid capability requested")
}
func (h *AlwaysTrueGuest) WriteHelloCap(ui terminal.UI) error {
return cap.WriteHello(ui)
}
```
A guest plugin may inherit the capabilities of a parent function by defining
a parent in the plugin implementation. This is done by setting the return
value of the `Parent` function to the name of the desired parent plugin. Go
based guest plugins may use Ruby based plugins as their parent.
```
// file: myplugin/guest/myguest.go
...
// ParentsFunc implements component.Guest
func (h *AlwaysTrueGuest) ParentFunc() interface{} {
return h.Parent
}
func (h *AlwaysTrueGuest) Parent() string {
// This plugin sets the parent to the "debian" plugin which is provided
// as a Ruby plugin. This AlwaysTrueGuest now inherits all the capabilities
// of the debian Ruby guest plugin.
return "debian"
}
```

View File

@ -0,0 +1,74 @@
---
layout: docs
page_title: Go Based Plugins
description: |-
Vagrant comes with many great features out of the box to get your environments
up and running. Sometimes, however, you want to change the way Vagrant does
something or add additional functionality to Vagrant. This can be done via
Vagrant plugins.
---
# Go Vagrant Plugins
With the introduction of Vagrant-go, Vagrant now supports running plugins
implemented in Go. Note that Vagrant-go can run Go and Ruby based plugins
while Vagrant-ruby only runs Ruby based plugins.
## Anatomy of a Go plugin
When a plugin is started, it runs in its own process and is able to communicate
with the other plugins over GRPC.
A Vagrant-go plugin must implement an interface for a [plugin component](https://github.com/hashicorp/vagrant-plugin-sdk/blob/main/component/component.go).
A plugin may satisfy more than one component interface. The plugin interfaces
define the `<name>Func` functions for the plugin. These functions are meant to
return the actual function that represents the named action. For example the
`Host` component defines a `DetectFunc`. So, a plugin must have a `DetectFunc`
implementation that returns a function that can detect if Vagrant is running
on the given host. For example
```
// DetectFunc implements component.Host
func (h *AlwaysTrueHost) DetectFunc() interface{} {
return h.Detect
}
func (h *AlwaysTrueHost) Detect() bool {
// This plugin always detects that it is running on the expected host
return true
}
```
Using this pattern of `<name>Func` functions, Vagrant is able to allow flexibility
over the receivers of the functions that actually define the behavior of the plugin.
This injection of dependencies is done using go-argmapper. So, in the same example,
the `Detect` function may be made to accept some arguments.
```
// DetectFunc implements component.Host
func (h *AlwaysTrueHost) DetectFunc() interface{} {
return h.Detect
}
func (h *AlwaysTrueHost) Detect(string msg, trm terminal.UI) bool {
trm.Output(msg)
return true
}
```
Now, in order to run the `Detect` function, the caller must provide a `string` and
`terminal.UI` argument. By default, Vagrant will always inject the following arguments
into a call to a plugin:
- `terminal.UI` component
- basis
- project (when available)
- context
- logger
For each component type, Vagrant may also inject some additional arguments.
Outside of these components, the caller must provide all other arguments that are required.
So, in the case above, in order to successfully call this `Detect` function, the caller must
also provide a `string`. It is recommended that plugin authors do not rely on arguments being
injected into their implementations outside of these sets of arguments.

View File

@ -699,6 +699,19 @@
{
"title": "Packaging &amp; Distribution",
"path": "plugins/packaging"
},
{
"title": "Go Plugins",
"routes": [
{
"title": "Overview",
"path": "plugins/go-plugins"
},
{
"title": "Guests",
"path": "plugins/go-plugins/guests"
}
]
}
]
},