packer-cn/website/content/docs/plugins/creation/custom-provisioners.mdx

128 lines
4.9 KiB
Plaintext
Raw Normal View History

2013-06-20 17:46:21 -04:00
---
2020-03-18 18:46:47 -04:00
description: >
Packer Provisioners are the components of Packer that install and configure
software into a running machine prior to turning that machine into an image.
An
example of a provisioner is the shell provisioner, which runs shell scripts
within the machines.
page_title: Custom Provisioners - Extending
---
2013-06-20 17:46:21 -04:00
# Custom Provisioners
2013-06-20 17:46:21 -04:00
Packer Provisioners are the components of Packer that install and configure
2015-07-22 22:31:00 -04:00
software into a running machine prior to turning that machine into an image. An
example of a provisioner is the [shell
2020-03-31 17:40:07 -04:00
provisioner](/docs/provisioners/shell), which runs shell scripts within
2018-10-26 20:02:51 -04:00
the machines.
2013-06-20 17:46:21 -04:00
2015-07-22 22:31:00 -04:00
Prior to reading this page, it is assumed you have read the page on [plugin
2020-03-31 17:40:07 -04:00
development basics](/docs/extending/plugins).
2013-06-20 17:46:21 -04:00
Provisioner plugins implement the [`packer.Provisioner`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#Provisioner) interface and are served
2015-07-22 22:31:00 -04:00
using the `plugin.ServeProvisioner` function.
2013-06-20 17:46:21 -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-20 17:46:21 -04:00
## The Interface
The interface that must be implemented for a provisioner is the
[`packer.Provisioner`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#Provisioner) interface. It is reproduced below for reference. The
2015-07-22 22:31:00 -04:00
actual interface in the source code contains some basic documentation as well
explaining what each method should do.
2020-03-18 18:46:47 -04:00
```go
2013-06-20 17:46:21 -04:00
type Provisioner interface {
2019-12-18 10:13:52 -05:00
ConfigSpec() hcldec.ObjectSpec
2015-07-22 22:31:00 -04:00
Prepare(...interface{}) error
2019-12-18 10:13:52 -05:00
Provision(context.Context, Ui, Communicator, map[string]interface{}) error
2013-06-20 17:46:21 -04:00
}
```
2013-06-20 17:46:21 -04:00
### The "Prepare" Method
2015-07-22 22:31:00 -04:00
The `Prepare` method for each provisioner is called prior to any runs with the
configuration that was given in the template. This is passed in as an array of
`interface{}` types, but is generally `map[string]interface{}`. The prepare
2013-06-20 17:46:21 -04:00
method is responsible for translating this configuration into an internal
structure, validating it, and returning any errors.
For multiple parameters, they should be merged together into the final
2018-10-26 20:02:51 -04:00
configuration, with later parameters overwriting any previous configuration.
The exact semantics of the merge are left to the builder author.
2013-06-20 17:46:21 -04:00
For decoding the `interface{}` into a meaningful structure, the
2015-07-22 22:31:00 -04:00
[mapstructure](https://github.com/mitchellh/mapstructure) library is
recommended. Mapstructure will take an `interface{}` and decode it into an
arbitrarily complex struct. If there are any errors, it generates very human
friendly errors that can be returned directly from the prepare method.
2013-06-20 17:46:21 -04:00
2018-10-26 20:02:51 -04:00
While it is not actively enforced, **no side effects** should occur from
running the `Prepare` method. Specifically, don't create files, don't launch
virtual machines, etc. Prepare's purpose is solely to configure the builder and
validate the configuration.
2013-06-20 17:46:21 -04:00
2015-07-22 22:31:00 -04:00
The `Prepare` method is called very early in the build process so that errors
may be displayed to the user before anything actually happens.
2013-06-20 17:46:21 -04:00
2019-12-20 15:05:01 -05:00
### The "ConfigSpec" Method
This method returns a hcldec.ObjectSpec, which is a spec necessary for using
HCL2 templates with Packer. For information on how to use and implement this
function, check our
[object spec docs](/guides/hcl/component-object-spec)
2019-12-20 15:05:01 -05:00
2013-06-20 17:46:21 -04:00
### The "Provision" Method
2015-07-22 22:31:00 -04:00
The `Provision` method is called when a machine is running and ready to be
provisioned. The provisioner should do its real work here.
2013-06-20 17:46:21 -04:00
2015-07-22 22:31:00 -04:00
The method takes two parameters: a `packer.Ui` and a `packer.Communicator`. The
UI can be used to communicate with the user what is going on. The communicator
is used to communicate with the running machine, and is guaranteed to be
connected at this point.
2013-06-20 17:46:21 -04:00
The provision method should not return until provisioning is complete.
2019-12-20 15:05:01 -05:00
The map[string]interface{} provides users with build-specific information,
like host and IP, provided by the `build` template engine. Provisioners may use
this information however they please, or not use it.
2013-06-20 17:46:21 -04:00
## Using the Communicator
2015-07-22 22:31:00 -04:00
The `packer.Communicator` parameter and interface is used to communicate with
running machine. The machine may be local (in a virtual machine or container of
some sort) or it may be remote (in a cloud). The communicator interface
abstracts this away so that communication is the same overall.
2013-06-20 17:46:21 -04:00
2015-07-22 22:31:00 -04:00
The documentation around the [code
itself](https://godoc.org/github.com/hashicorp/packer-plugin-sdk/packer#Communicator)
2015-07-22 22:31:00 -04:00
is really great as an overview of how to use the interface. You should begin by
reading this. Once you have read it, you can see some example usage below:
2013-06-20 17:46:21 -04:00
2020-03-18 18:46:47 -04:00
```go
2013-06-20 17:46:21 -04:00
// Build the remote command.
var cmd packer.RemoteCmd
cmd.Command = "echo foo"
// We care about stdout, so lets collect that into a buffer. Since
// we don't set stderr, that will just be discarded.
var stdout bytes.Buffer
cmd.Stdout = &stdout
// Start the command
if err := comm.Start(&cmd); err != nil {
panic(err)
}
// Wait for it to complete
cmd.Wait()
// Read the stdout!
fmt.Printf("Command output: %s", stdout.String())
```