Commit f0a09ffa authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

Crazy things with RPC servers and stuff

parent 0985d261
package rpc
import (
"github.com/mitchellh/packer/packer"
"net/rpc"
)
// An implementation of packer.Build where the build is actually executed
// over an RPC connection.
type Build struct {
client *rpc.Client
}
// BuildServer wraps a packer.Build implementation and makes it exportable
// as part of a Golang RPC server.
type BuildServer struct {
build packer.Build
}
type BuildPrepareArgs interface{}
type BuildRunArgs struct {
UiRPCAddress string
}
func (b *Build) Prepare() {
b.client.Call("Build.Prepare", new(interface{}), new(interface{}))
}
func (b *Build) Run(ui packer.Ui) {
// Create and start the server for the UI
server := NewServer()
server.RegisterUi(ui)
server.Start()
defer server.Stop()
args := &BuildRunArgs{server.Address()}
b.client.Call("Build.Run", args, new(interface{}))
}
func (b *BuildServer) Prepare(args *BuildPrepareArgs, reply *interface{}) error {
b.build.Prepare()
*reply = nil
return nil
}
func (b *BuildServer) Run(args *BuildRunArgs, reply *interface{}) error {
client, err := rpc.Dial("tcp", args.UiRPCAddress)
if err != nil {
return err
}
b.build.Run(&Ui{client})
*reply = nil
return nil
}
package rpc
import (
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"net/rpc"
"testing"
)
type testBuild struct {
prepareCalled bool
runCalled bool
runUi packer.Ui
}
func (b *testBuild) Prepare() {
b.prepareCalled = true
}
func (b *testBuild) Run(ui packer.Ui) {
b.runCalled = true
b.runUi = ui
}
func TestBuildRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the UI to test
b := new(testBuild)
bServer := &BuildServer{b}
// Start the RPC server
readyChan := make(chan int)
stopChan := make(chan int)
defer func() { stopChan <- 1 }()
go testRPCServer(":1234", "Build", bServer, 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)
}
// Test Prepare
bClient := &Build{client}
bClient.Prepare()
assert.True(b.prepareCalled, "prepare should be called")
// Test Run
ui := new(testUi)
bClient.Run(ui)
assert.True(b.runCalled, "run should be called")
// Test the UI given to run, which should be fully functional
if b.runCalled {
b.runUi.Say("format")
assert.True(ui.sayCalled, "say should be called")
assert.Equal(ui.sayFormat, "format", "format should be correct")
}
}
func TestBuild_ImplementsBuild(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
var realBuild packer.Build
b := &Build{nil}
assert.Implementor(b, &realBuild, "should be a Build")
}
package rpc
import (
"errors"
"github.com/mitchellh/packer/packer"
"net"
"net/rpc"
)
// A Server is a Golang RPC server that has helper methods for automatically
// setting up the endpoints for Packer interfaces.
type Server struct {
server *rpc.Server
started bool
doneChan chan bool
}
// Creates and returns a new Server.
func NewServer() *Server {
return &Server{
server: rpc.NewServer(),
started: false,
}
}
func (s *Server) Address() string {
return ":2345"
}
func (s *Server) RegisterUi(ui packer.Ui) {
s.server.RegisterName("Ui", &UiServer{ui})
}
func (s *Server) Start() error {
if s.started {
return errors.New("Server already started.")
}
// TODO: Address
address := ":2345"
// Mark that we started and setup the channel we'll use to mark exits
s.started = true
s.doneChan = make(chan bool)
// Start the TCP listener and a goroutine responsible for cleaning up the
// listener.
listener, _ := net.Listen("tcp", address)
go func() {
<-s.doneChan
listener.Close()
}()
// Start accepting connections
go func() {
for {
conn, err := listener.Accept()
if err != nil {
break
}
go s.server.ServeConn(conn)
}
}()
return nil
}
func (s *Server) Stop() {
if s.started {
// TODO: There is a race condition here, we need to wait for
// the listener to REALLY close.
s.doneChan <- true
s.started = false
s.doneChan = nil
}
}
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