packer/rpc: Support Hooks
This commit is contained in:
parent
8ed313e7b5
commit
30ab944437
|
@ -6,7 +6,8 @@ package packer
|
||||||
// Run is called when the hook is called, with the name of the hook and
|
// Run is called when the hook is called, with the name of the hook and
|
||||||
// arbitrary data associated with it. To know what format the data is in,
|
// arbitrary data associated with it. To know what format the data is in,
|
||||||
// you must reference the documentation for the specific hook you're interested
|
// you must reference the documentation for the specific hook you're interested
|
||||||
// in.
|
// in. In addition to that, the Hook is given access to a UI so that it can
|
||||||
|
// output things to the user.
|
||||||
type Hook interface {
|
type Hook interface {
|
||||||
Run(string, interface{})
|
Run(string, interface{}, Ui)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ type TestHook struct {
|
||||||
runCalled bool
|
runCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestHook) Run(string, interface{}) {
|
func (t *TestHook) Run(string, interface{}, Ui) {
|
||||||
t.runCalled = true
|
t.runCalled = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,23 @@ func (e *Environment) Cli(args []string) (result int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Environment) Hook(name string) (h packer.Hook, err error) {
|
||||||
|
var reply string
|
||||||
|
err = e.client.Call("Environment.Hook", name, &reply)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = rpc.Dial("tcp", reply)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Hook
|
||||||
|
h = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Environment) Ui() packer.Ui {
|
func (e *Environment) Ui() packer.Ui {
|
||||||
var reply string
|
var reply string
|
||||||
e.client.Call("Environment.Ui", new(interface{}), &reply)
|
e.client.Call("Environment.Ui", new(interface{}), &reply)
|
||||||
|
@ -71,6 +88,20 @@ func (e *EnvironmentServer) Cli(args *EnvironmentCliArgs, reply *int) (err error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EnvironmentServer) Hook(name *string, reply *string) error {
|
||||||
|
_, err := e.env.Hook(*name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap it
|
||||||
|
// TODO: Register hook
|
||||||
|
server := rpc.NewServer()
|
||||||
|
|
||||||
|
*reply = serveSingleConn(server)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error {
|
func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error {
|
||||||
ui := e.env.Ui()
|
ui := e.env.Ui()
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ type testEnvironment struct {
|
||||||
builderName string
|
builderName string
|
||||||
cliCalled bool
|
cliCalled bool
|
||||||
cliArgs []string
|
cliArgs []string
|
||||||
|
hookCalled bool
|
||||||
|
hookName string
|
||||||
uiCalled bool
|
uiCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +32,12 @@ func (e *testEnvironment) Cli(args []string) (int, error) {
|
||||||
return 42, nil
|
return 42, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *testEnvironment) Hook(name string) (packer.Hook, error) {
|
||||||
|
e.hookCalled = true
|
||||||
|
e.hookName = name
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *testEnvironment) Ui() packer.Ui {
|
func (e *testEnvironment) Ui() packer.Ui {
|
||||||
e.uiCalled = true
|
e.uiCalled = true
|
||||||
return testEnvUi
|
return testEnvUi
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"net/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An implementation of packer.Hook where the hook is actually executed
|
||||||
|
// over an RPC connection.
|
||||||
|
type hook struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// HookServer wraps a packer.Hook implementation and makes it exportable
|
||||||
|
// as part of a Golang RPC server.
|
||||||
|
type HookServer struct {
|
||||||
|
hook packer.Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
type HookRunArgs struct {
|
||||||
|
Name string
|
||||||
|
Data interface{}
|
||||||
|
RPCAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hook(client *rpc.Client) *hook {
|
||||||
|
return &hook{client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hook) Run(name string, data interface{}, ui packer.Ui) {
|
||||||
|
server := rpc.NewServer()
|
||||||
|
RegisterUi(server, ui)
|
||||||
|
address := serveSingleConn(server)
|
||||||
|
|
||||||
|
args := &HookRunArgs{name, data, address}
|
||||||
|
h.client.Call("Hook.Run", args, new(interface{}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error {
|
||||||
|
client, err := rpc.Dial("tcp", args.RPCAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.hook.Run(args.Name, args.Data, &Ui{client})
|
||||||
|
|
||||||
|
*reply = nil
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cgl.tideland.biz/asserts"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"net/rpc"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testHook struct {
|
||||||
|
runCalled bool
|
||||||
|
runUi packer.Ui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *testHook) Run(name string, data interface{}, ui packer.Ui) {
|
||||||
|
h.runCalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookRPC(t *testing.T) {
|
||||||
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
|
// Create the UI to test
|
||||||
|
h := new(testHook)
|
||||||
|
|
||||||
|
// Serve
|
||||||
|
server := rpc.NewServer()
|
||||||
|
RegisterHook(server, h)
|
||||||
|
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")
|
||||||
|
|
||||||
|
hClient := Hook(client)
|
||||||
|
|
||||||
|
// Test Run
|
||||||
|
ui := &testUi{}
|
||||||
|
hClient.Run("foo", 42, ui)
|
||||||
|
assert.True(h.runCalled, "run should be called")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHook_Implements(t *testing.T) {
|
||||||
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
|
var r packer.Hook
|
||||||
|
h := &hook{nil}
|
||||||
|
|
||||||
|
assert.Implementor(h, &r, "should be a Hook")
|
||||||
|
}
|
|
@ -29,6 +29,12 @@ func RegisterEnvironment(s *rpc.Server, e packer.Environment) {
|
||||||
s.RegisterName("Environment", &EnvironmentServer{e})
|
s.RegisterName("Environment", &EnvironmentServer{e})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Registers the appropriate endpoint on an RPC server to serve a
|
||||||
|
// Hook.
|
||||||
|
func RegisterHook(s *rpc.Server, hook packer.Hook) {
|
||||||
|
s.RegisterName("Hook", &HookServer{hook})
|
||||||
|
}
|
||||||
|
|
||||||
// Registers the appropriate endpoint on an RPC server to serve a
|
// Registers the appropriate endpoint on an RPC server to serve a
|
||||||
// Packer UI
|
// Packer UI
|
||||||
func RegisterUi(s *rpc.Server, ui packer.Ui) {
|
func RegisterUi(s *rpc.Server, ui packer.Ui) {
|
||||||
|
|
Loading…
Reference in New Issue