Commit 92a4f278 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer/rpc: Support provisioners

parent 638e1911
......@@ -59,6 +59,22 @@ func (e *Environment) Hook(name string) (h packer.Hook, err error) {
return
}
func (e *Environment) Provisioner(name string) (p packer.Provisioner, err error) {
var reply string
err = e.client.Call("Environment.Provisioner", name, &reply)
if err != nil {
return
}
client, err := rpc.Dial("tcp", reply)
if err != nil {
return
}
p = Provisioner(client)
return
}
func (e *Environment) Ui() packer.Ui {
var reply string
e.client.Call("Environment.Ui", new(interface{}), &reply)
......@@ -101,6 +117,19 @@ func (e *EnvironmentServer) Hook(name *string, reply *string) error {
return nil
}
func (e *EnvironmentServer) Provisioner(name *string, reply *string) error {
prov, err := e.env.Provisioner(*name)
if err != nil {
return err
}
server := rpc.NewServer()
RegisterProvisioner(server, prov)
*reply = serveSingleConn(server)
return nil
}
func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error {
ui := e.env.Ui()
......
......@@ -17,6 +17,8 @@ type testEnvironment struct {
cliArgs []string
hookCalled bool
hookName string
provCalled bool
provName string
uiCalled bool
}
......@@ -38,6 +40,12 @@ func (e *testEnvironment) Hook(name string) (packer.Hook, error) {
return nil, nil
}
func (e *testEnvironment) Provisioner(name string) (packer.Provisioner, error) {
e.provCalled = true
e.provName = name
return nil, nil
}
func (e *testEnvironment) Ui() packer.Ui {
e.uiCalled = true
return testEnvUi
......@@ -74,6 +82,11 @@ func TestEnvironmentRPC(t *testing.T) {
assert.Equal(e.cliArgs, cliArgs, "args should match")
assert.Equal(result, 42, "result shuld be 42")
// Test Provisioner
_, _ = eClient.Provisioner("foo")
assert.True(e.provCalled, "provisioner should be called")
assert.Equal(e.provName, "foo", "should have proper name")
// Test Ui
ui := eClient.Ui()
assert.True(e.uiCalled, "Ui should've been called")
......
package rpc
import (
"github.com/mitchellh/packer/packer"
"net/rpc"
)
// An implementation of packer.Provisioner where the provisioner is actually
// executed over an RPC connection.
type provisioner struct {
client *rpc.Client
}
// ProvisionerServer wraps a packer.Provisioner implementation and makes it
// exportable as part of a Golang RPC server.
type ProvisionerServer struct {
p packer.Provisioner
}
type ProvisionerPrepareArgs struct {
Config interface{}
RPCAddress string
}
type ProvisionerProvisionArgs struct {
RPCAddress string
}
func Provisioner(client *rpc.Client) *provisioner {
return &provisioner{client}
}
func (p *provisioner) Prepare(config interface{}, ui packer.Ui) {
// TODO: Error handling
server := rpc.NewServer()
RegisterUi(server, ui)
args := &ProvisionerPrepareArgs{config, serveSingleConn(server)}
p.client.Call("Provisioner.Prepare", args, new(interface{}))
}
func (p *provisioner) Provision(ui packer.Ui, comm packer.Communicator) {
// TODO: Error handling
server := rpc.NewServer()
RegisterCommunicator(server, comm)
RegisterUi(server, ui)
args := &ProvisionerProvisionArgs{serveSingleConn(server)}
p.client.Call("Provisioner.Provision", args, new(interface{}))
}
func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interface{}) error {
client, err := rpc.Dial("tcp", args.RPCAddress)
if err != nil {
return err
}
ui := &Ui{client}
p.p.Prepare(args.Config, ui)
return nil
}
func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *interface{}) error {
client, err := rpc.Dial("tcp", args.RPCAddress)
if err != nil {
return err
}
comm := Communicator(client)
ui := &Ui{client}
p.p.Provision(ui, comm)
return nil
}
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"testing"
)
type testProvisioner struct {
prepareCalled bool
prepareConfig interface{}
prepareUi packer.Ui
provCalled bool
provComm packer.Communicator
provUi packer.Ui
}
func (p *testProvisioner) Prepare(config interface{}, ui packer.Ui) {
p.prepareCalled = true
p.prepareConfig = config
p.prepareUi = ui
}
func (p *testProvisioner) Provision(ui packer.Ui, comm packer.Communicator) {
p.provCalled = true
p.provComm = comm
p.provUi = ui
}
func TestProvisionerRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
p := new(testProvisioner)
// Start the server
server := rpc.NewServer()
RegisterProvisioner(server, p)
address := serveSingleConn(server)
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
assert.Nil(err, "should be able to connect")
// Test Prepare
config := 42
ui := &testUi{}
pClient := Provisioner(client)
pClient.Prepare(config, ui)
assert.True(p.prepareCalled, "prepare should be called")
assert.Equal(p.prepareConfig, 42, "prepare should be called with right arg")
p.prepareUi.Say("foo")
assert.True(ui.sayCalled, "say should be called")
// Test Provision
ui = &testUi{}
comm := &testCommunicator{}
pClient.Provision(ui, comm)
assert.True(p.provCalled, "provision should be called")
p.provUi.Say("foo")
assert.True(ui.sayCalled, "say should be called")
}
func TestProvisioner_Implements(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
var r packer.Provisioner
p := Provisioner(nil)
assert.Implementor(p, &r, "should be a provisioner")
}
......@@ -47,6 +47,11 @@ func RegisterHook(s *rpc.Server, hook packer.Hook) {
s.RegisterName("Hook", &HookServer{hook})
}
// Registers the appropriate endpoint on an RPC server to serve a packer.Provisioner
func RegisterProvisioner(s *rpc.Server, p packer.Provisioner) {
s.RegisterName("Provisioner", &ProvisionerServer{p})
}
// Registers the appropriate endpoint on an RPC server to serve a
// Packer UI
func RegisterUi(s *rpc.Server, ui packer.Ui) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment