This commit is contained in:
Mitchell Hashimoto 2013-05-03 15:49:15 -07:00
parent 94cdedf40f
commit 8dea720627
4 changed files with 141 additions and 0 deletions

18
packer/rpc/environment.go Normal file
View File

@ -0,0 +1,18 @@
package rpc
import (
"github.com/mitchellh/packer/packer"
"net/rpc"
)
// A EnvironmentClient is an implementation of the packer.Environment interface
// where the actual environment is executed over an RPC connection.
type EnvironmentClient struct {
client *rpc.Client
}
// A EnvironmentServer wraps a packer.Environment and makes it exportable
// as part of a Golang RPC server.
type EnvironmentServer struct {
env packer.Environment
}

43
packer/rpc/rpc_test.go Normal file
View File

@ -0,0 +1,43 @@
package rpc
import (
"net"
"net/rpc"
)
// This starts a RPC server for the given interface listening on the
// given address. The RPC server is ready when "readyChan" receives a message
// and the RPC server will quit when "stopChan" receives a message.
//
// This function should be run in a goroutine.
func testRPCServer(laddr string, name string, iface interface{}, readyChan chan int, stopChan <-chan int) {
listener, err := net.Listen("tcp", laddr)
if err != nil {
panic(err)
}
// Close the listener when we exit so that the RPC server ends
defer listener.Close()
// Start the RPC server
server := rpc.NewServer()
server.RegisterName(name, iface)
go func() {
for {
conn, err := listener.Accept()
if err != nil {
// If there is an error, just ignore it.
break
}
go server.ServeConn(conn)
}
}()
// We're ready!
readyChan <- 1
// Block on waiting to receive from the channel
<-stopChan
}

35
packer/rpc/ui.go Normal file
View File

@ -0,0 +1,35 @@
package rpc
import (
"github.com/mitchellh/packer/packer"
"net/rpc"
)
// An implementation of packer.Ui where the Ui is actually executed
// over an RPC connection.
type Ui struct {
client *rpc.Client
}
// UiServer wraps a packer.Ui implementation and makes it exportable
// as part of a Golang RPC server.
type UiServer struct {
ui packer.Ui
}
type UiSayArgs struct {
Format string
Vars []interface{}
}
func (u *Ui) Say(format string, a ...interface{}) {
args := &UiSayArgs{format, a}
u.client.Call("Ui.Say", args, new(interface{}))
}
func (u *UiServer) Say(args *UiSayArgs, reply *interface{}) error {
u.ui.Say(args.Format, args.Vars...)
*reply = nil
return nil
}

45
packer/rpc/ui_test.go Normal file
View File

@ -0,0 +1,45 @@
package rpc
import (
"cgl.tideland.biz/asserts"
"net/rpc"
"testing"
)
type testUi struct {
sayCalled bool
sayFormat string
sayVars []interface{}
}
func (u *testUi) Say(format string, a ...interface{}) {
u.sayCalled = true
u.sayFormat = format
u.sayVars = a
}
func TestUiRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the UI to test
ui := new(testUi)
uiServer := &UiServer{ui}
// Start the RPC server
readyChan := make(chan int)
stopChan := make(chan int)
defer func() { stopChan <- 1 }()
go testRPCServer(":1234", "Ui", uiServer, readyChan, stopChan)
<-readyChan
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", ":1234")
if err != nil {
panic(err)
}
uiClient := &Ui{client}
uiClient.Say("format", "arg0", 42)
assert.Equal(ui.sayFormat, "format", "format should be correct")
}