packer/rpc: Support provisioners
This commit is contained in:
parent
638e191186
commit
92a4f27868
|
@ -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")
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
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) {
|
||||
|
|
Loading…
Reference in New Issue