packer/plugin: Support provisioners
This commit is contained in:
parent
1b78fc88a9
commit
37372bac93
|
@ -110,3 +110,15 @@ func ServeHook(hook packer.Hook) {
|
|||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Serves a provisioner from a plugin.
|
||||
func ServeProvisioner(p packer.Provisioner) {
|
||||
log.Println("Preparing to serve a provisioner plugin...")
|
||||
|
||||
server := rpc.NewServer()
|
||||
packrpc.RegisterProvisioner(server, p)
|
||||
|
||||
if err := serve(server); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ func TestHelperProcess(*testing.T) {
|
|||
ServeHook(new(helperHook))
|
||||
case "invalid-rpc-address":
|
||||
fmt.Println("lolinvalid")
|
||||
case "provisioner":
|
||||
ServeProvisioner(new(helperProvisioner))
|
||||
case "start-timeout":
|
||||
time.Sleep(1 * time.Minute)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
packrpc "github.com/mitchellh/packer/packer/rpc"
|
||||
"log"
|
||||
"net/rpc"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type cmdProvisioner struct {
|
||||
p packer.Provisioner
|
||||
client *client
|
||||
}
|
||||
|
||||
func (c *cmdProvisioner) Prepare(config interface{}, ui packer.Ui) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
c.checkExit(r, nil)
|
||||
}()
|
||||
|
||||
c.p.Prepare(config, ui)
|
||||
}
|
||||
|
||||
func (c *cmdProvisioner) Provision(ui packer.Ui, comm packer.Communicator) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
c.checkExit(r, nil)
|
||||
}()
|
||||
|
||||
c.p.Provision(ui, comm)
|
||||
}
|
||||
|
||||
func (c *cmdProvisioner) checkExit(p interface{}, cb func()) {
|
||||
if c.client.Exited() {
|
||||
cb()
|
||||
} else if p != nil {
|
||||
log.Panic(p)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a valid packer.Provisioner where the hook is executed via RPC
|
||||
// to a plugin that is within a subprocess.
|
||||
//
|
||||
// This method will start the given exec.Cmd, which should point to
|
||||
// the plugin binary to execute. Some configuration will be done to
|
||||
// the command, such as overriding Stdout and some environmental variables.
|
||||
//
|
||||
// This function guarantees the subprocess will end in a timely manner.
|
||||
func Provisioner(cmd *exec.Cmd) (result packer.Provisioner, err error) {
|
||||
cmdClient := NewManagedClient(cmd)
|
||||
address, err := cmdClient.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Make sure the command is properly killed in the case of an error
|
||||
if err != nil {
|
||||
cmdClient.Kill()
|
||||
}
|
||||
}()
|
||||
|
||||
client, err := rpc.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result = &cmdProvisioner{
|
||||
packrpc.Provisioner(client),
|
||||
cmdClient,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"cgl.tideland.biz/asserts"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type helperProvisioner byte
|
||||
|
||||
func (helperProvisioner) Prepare(interface{}, packer.Ui) {}
|
||||
|
||||
func (helperProvisioner) Provision(packer.Ui, packer.Communicator) {}
|
||||
|
||||
func TestProvisioner_NoExist(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
_, err := Provisioner(exec.Command("i-should-never-ever-ever-exist"))
|
||||
assert.NotNil(err, "should have an error")
|
||||
}
|
||||
|
||||
func TestProvisioner_Good(t *testing.T) {
|
||||
assert := asserts.NewTestingAsserts(t, true)
|
||||
|
||||
_, err := Provisioner(helperProcess("provisioner"))
|
||||
assert.Nil(err, "should start provisioner properly")
|
||||
}
|
Loading…
Reference in New Issue