packer-cn/website/content/docs/plugins/creation/index.mdx

245 lines
10 KiB
Plaintext
Raw Normal View History

2013-06-11 00:47:16 -04:00
---
2017-06-14 21:04:16 -04:00
description: |
Packer is designed to be extensible. Because the surface area for workloads is
infinite, Packer supports plugins for builders, provisioners, and
post-processors.
page_title: Extending
sidebar_title: Extending Packer
---
# Extending Packer
Packer is designed to be extensible. Because the surface area for workloads is
infinite, Packer supports plugins for builders, provisioners, and
post-processors. To learn more about the different customizations, please
choose a link from the sidebar.
## Developing Plugins
2013-06-11 00:47:16 -04:00
2015-07-22 22:31:00 -04:00
This page will document how you can develop your own Packer plugins. Prior to
reading this, it is assumed that you're comfortable with Packer and also know
the [basics of how Plugins work](/docs/plugins), from a user
2015-07-22 22:31:00 -04:00
standpoint.
2013-06-11 00:47:16 -04:00
2016-01-14 15:31:19 -05:00
Packer plugins must be written in [Go](https://golang.org/), so it is also
2015-07-22 22:31:00 -04:00
assumed that you're familiar with the language. This page will not be a Go
language tutorial. Thankfully, if you are familiar with Go, the Go toolchain
provides many conveniences to help to develop Packer plugins.
2013-06-11 00:47:16 -04:00
2020-03-23 20:02:12 -04:00
~> **Warning!** This is an advanced topic. If you're new to Packer, we
recommend getting a bit more comfortable before you dive into writing plugins.
2013-06-11 00:47:16 -04:00
### Plugin System Architecture
2013-06-11 00:47:16 -04:00
Packer has a fairly unique plugin architecture. Instead of loading plugins
2020-03-18 18:46:47 -04:00
directly into a running application, Packer runs each plugin as a _separate
application_. Inter-process communication and RPC is then used to communicate
2018-10-26 20:02:51 -04:00
between the many running Packer processes. Packer core itself is responsible
for orchestrating the processes and handles cleanup.
2013-06-11 00:47:16 -04:00
The beauty of this is that your plugin can have any dependencies it wants.
Dependencies don't need to line up with what Packer core or any other plugin
2015-07-22 22:31:00 -04:00
uses, because they're completely isolated into the process space of the plugin
itself.
2013-06-11 00:47:16 -04:00
2015-07-22 22:31:00 -04:00
And, thanks to Go's
2016-01-14 15:31:19 -05:00
[interfaces](https://golang.org/doc/effective_go.html#interfaces_and_types), it
2015-07-22 22:31:00 -04:00
doesn't even look like inter-process communication is occurring. You just use
the interfaces like normal, but in fact they're being executed in a remote
process. Pretty cool.
2013-06-11 00:47:16 -04:00
### Plugin Development Basics
2013-06-11 00:47:16 -04:00
Developing a plugin allows you to create additional functionality for Packer.
2018-10-26 20:02:51 -04:00
All the various kinds of plugins have a corresponding interface. The plugin
needs to implement this interface and expose it using the Packer plugin package
(covered here shortly), and that's it!
2013-06-11 00:47:16 -04:00
2018-10-26 20:02:51 -04:00
There are two packages that really matter that every plugin must use. Other
than the following two packages, you're encouraged to use whatever packages you
want. Because plugins are their own processes, there is no danger of colliding
2015-07-22 22:31:00 -04:00
dependencies.
2013-06-11 00:47:16 -04:00
- [`github.com/hashicorp/packer-plugin-sdk/packer`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer) - Contains all the interfaces that you have
2020-03-18 18:46:47 -04:00
to implement for any given plugin.
2013-06-11 00:47:16 -04:00
- [`github.com/hashicorp/packer-plugin-sdk/plugin`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/plugin) - Contains the code to serve
2020-03-18 18:46:47 -04:00
the plugin. This handles all the inter-process communication stuff.
2013-06-11 00:47:16 -04:00
There are two steps involved in creating a plugin:
2015-07-22 23:25:58 -04:00
1. Implement the desired interface. For example, if you're building a builder
plugin, implement the [packer.Builder](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#Builder) interface.
2013-06-11 00:47:16 -04:00
2. Serve the interface by calling the appropriate [plugin](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/plugin#Set.RegisterBuilder) serving method in
your main method.
2013-06-11 00:47:16 -04:00
Basic examples are shown below. Note that if you can define a multi-plugin
binary as it will allow to add more that one plugin per binary.
<Tabs>
<Tab heading="Multi-plugin Binary">
2013-06-11 00:47:16 -04:00
2020-03-18 18:46:47 -04:00
```go
2013-06-11 00:47:16 -04:00
import (
2020-12-17 16:29:25 -05:00
"github.com/hashicorp/packer-plugin-sdk/plugin"
2013-06-11 00:47:16 -04:00
)
// Assume this implements packer.Builder
type ExampleBuilder struct{}
// Assume this implements packer.PostProcessor
type FooPostProcessor struct{}
// Assume this implements packer.Provisioner
type BarProvisioner struct{}
2013-06-11 00:47:16 -04:00
func main() {
pps := plugin.NewSet()
pps.RegisterBuilder("example", new(ExampleBuilder))
pps.RegisterPostProcessor("foo", new(FooPostProcessor))
pps.RegisterProvisioner("bar", new(BarProvisioner))
err := pps.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
2013-06-11 00:47:16 -04:00
}
```
2013-06-11 00:47:16 -04:00
**That's it!** `plugin.NewSet` handles all the nitty gritty of
2015-07-22 22:31:00 -04:00
communicating with Packer core and serving your builder over RPC. It can't get
much easier than that.
2013-06-11 00:47:16 -04:00
Here the name of the plugin will be used to use each plugin, so if your plugin
is named `packer-plugin-my`, this would make the following parts available:
* the `my-example` builder
* the `my-foo` post-processor
* the `my-bar` provisioner
</Tab>
<Tab heading="Single Plugin Binary">
```go
import (
"github.com/hashicorp/packer-plugin-sdk/plugin"
)
// Assume this implements packer.Builder
type Builder struct{}
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterBuilder(new(Builder))
server.Serve()
}
```
**That's it!** `plugin.Server` handles all the nitty gritty of communicating with
Packer core and serving your builder over RPC. It can't get much easier than
that.
</Tab>
</Tabs>
2015-07-22 22:31:00 -04:00
Next, just build your plugin like a normal Go application, using `go build` or
however you please. The resulting binary is the plugin that can be installed
using standard installation procedures.
2013-06-11 00:47:16 -04:00
2015-07-22 22:31:00 -04:00
The specifics of how to implement each type of interface are covered in the
relevant subsections available in the navigation to the left.
2013-06-11 00:47:16 -04:00
2020-03-23 20:02:12 -04:00
~> **Lock your dependencies!** Using `go mod` is highly recommended since
2018-01-24 19:59:32 -05:00
the Packer codebase will continue to improve, potentially breaking APIs along
the way until there is a stable release. By locking your dependencies, your
plugins will continue to work with the version of Packer you lock to.
### Logging and Debugging
2013-06-11 00:47:16 -04:00
2018-10-26 20:02:51 -04:00
Plugins can use the standard Go `log` package to log. Anything logged using
this will be available in the Packer log files automatically. The Packer log is
visible on stderr when the `PACKER_LOG` environment var is set.
2013-06-11 00:47:16 -04:00
2018-10-26 20:02:51 -04:00
Packer will prefix any logs from plugins with the path to that plugin to make
it identifiable where the logs come from. Some example logs are shown below:
2013-06-11 00:47:16 -04:00
2020-03-18 18:46:47 -04:00
```text
2017-07-04 10:39:41 -04:00
2013/06/10 21:44:43 Loading builder: custom
2013/06/10 21:44:43 packer-builder-custom: 2013/06/10 21:44:43 Plugin minimum port: 10000
2013/06/10 21:44:43 packer-builder-custom: 2013/06/10 21:44:43 Plugin maximum port: 25000
2013/06/10 21:44:43 packer-builder-custom: 2013/06/10 21:44:43 Plugin address: :10000
2013-06-11 00:47:16 -04:00
```
2017-07-04 10:41:40 -04:00
As you can see, the log messages from the custom builder plugin are prefixed
2020-03-18 18:46:47 -04:00
with "packer-builder-custom". Log output is _extremely_ helpful in debugging
2018-10-26 20:02:51 -04:00
issues and you're encouraged to be as verbose as you need to be in order for
the logs to be helpful.
2013-06-11 00:47:16 -04:00
2020-12-15 04:28:17 -05:00
### Creating a GitHub Release
2020-12-15 06:38:03 -05:00
To create a GitHub release with the assets that will be consumed by Packer, we provide a pre-defined release workflow configuration
using [GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions). We strongly encourage maintainers to use this configuration
2020-12-15 08:59:25 -05:00
to make sure the release contains the assets expected by Packer to load and use a plugin.
2020-12-15 04:28:17 -05:00
[GitHub Actions](https://docs.github.com/en/free-pro-team@latest/actions) allow you to execute workflows when events on your repository occur.
You can use this to create releases whenever a new version tag is created on your repository.
2020-12-15 06:38:03 -05:00
Here's what you need to create releases using GitHub Actions:
2020-12-15 04:28:17 -05:00
1. Generate a GPG key to be used when signing releases (See [GitHub's detailed instructions](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/generating-a-new-gpg-key)
for help with this step)
2. Copy the [GoReleaser configuration from the packer-plugin-scaffolding repository](https://github.com/hashicorp/packer-plugin-scaffolding/blob/main/.goreleaser.yml) to the root of your repository.
3. Copy the [GitHub Actions workflow from the packer-plugin-scaffolding repository](https://github.com/hashicorp/packer-plugin-scaffolding/blob/main/.github/workflows/release.yml) to `.github/workflows/release.yml` in your repository.
An easier way to execute steps 1 and 2, is by running the following command in the root folder of your plugin to download
and place both files in their expected place:
```shell
curl -L -o .goreleaser.yml https://raw.githubusercontent.com/hashicorp/packer-plugin-scaffolding/main/.goreleaser.yml &&
mkdir -p .github/workflows &&
curl -L -o .github/workflows/release.yml https://raw.githubusercontent.com/hashicorp/packer-plugin-scaffolding/main/.github/workflows/release.yml
```
2020-12-15 04:28:17 -05:00
4. Go to Settings > Secrets in your repository, and add the following secrets:
- `GPG_PRIVATE_KEY` - Your ASCII-armored GPG private key. You can export this with `gpg --armor --export-secret-keys [key ID or email]`.
- `PASSPHRASE` - The passphrase for your GPG private key.
2020-12-15 06:38:03 -05:00
5. Push a new valid version tag (e.g. `v1.2.3`) to test that the GitHub Actions releaser is working. The tag must be a valid [Semantic Version](https://semver.org/) preceded with a `v`.
2020-12-15 04:28:17 -05:00
### Plugin Development Tips
2013-06-11 00:47:16 -04:00
2015-07-22 22:31:00 -04:00
Here are some tips for developing plugins, often answering common questions or
concerns.
2013-06-11 00:47:16 -04:00
#### Naming Conventions
2013-06-20 17:30:02 -04:00
2015-07-22 22:31:00 -04:00
It is standard practice to name the resulting plugin application in the format
of `packer-plugin-NAME`. For example, if you're building a new builder for
2015-07-22 22:31:00 -04:00
CustomCloud, it would be standard practice to name the resulting plugin
`packer-plugin-custom-cloud`. This naming convention helps users identify the
scope of a plugin.
2013-06-11 00:47:16 -04:00
#### Testing Plugins
2013-06-20 17:30:02 -04:00
Making your unpublished plugin available to Packer is possible by either :
2013-06-11 00:47:16 -04:00
* Starting Packer from the directory where the plugin binary is located.
* Putting the plugin binary in the same directory as Packer.
In both these cases, if the binary is called `packer-plugin-myawesomecloud` and
defines an `ebs` builder then you will be able to use an `myawesomecloud-ebs`
builder or source without needing to have a `required_plugin` block.
2013-06-11 00:47:16 -04:00
This is extremely useful during development.
2013-06-11 00:47:16 -04:00
#### Distributing Plugins
2013-06-20 17:30:02 -04:00
It is recommended you use a tool like the GoReleaser in order to cross-compile
your plugin for every platform that Packer supports, since Go applications are
platform-specific. If you have created your plugin from the
[packer-plugin-scaffolding](https://github.com/hashicorp/packer-plugin-scaffolding)
repo, simply tagging a commit will correctly build and release the binaries using GoReleaser.