Commit b9e3eb1f authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer/rpc: Get rid of the heavy server stuff

parent 0cc3a5f9
...@@ -30,12 +30,9 @@ func (b *Build) Prepare() { ...@@ -30,12 +30,9 @@ func (b *Build) Prepare() {
func (b *Build) Run(ui packer.Ui) { func (b *Build) Run(ui packer.Ui) {
// Create and start the server for the UI // Create and start the server for the UI
// TODO: Error handling // TODO: Error handling
server := NewServer() server := rpc.NewServer()
server.RegisterUi(ui) RegisterUi(server, ui)
server.Start() args := &BuildRunArgs{serveSingleConn(server)}
defer server.Stop()
args := &BuildRunArgs{server.Address()}
b.client.Call("Build.Run", args, new(interface{})) b.client.Call("Build.Run", args, new(interface{}))
} }
......
...@@ -32,13 +32,11 @@ func (b *Builder) Prepare(config interface{}) { ...@@ -32,13 +32,11 @@ func (b *Builder) Prepare(config interface{}) {
func (b *Builder) Run(build packer.Build, ui packer.Ui) { func (b *Builder) Run(build packer.Build, ui packer.Ui) {
// Create and start the server for the Build and UI // Create and start the server for the Build and UI
// TODO: Error handling // TODO: Error handling
server := NewServer() server := rpc.NewServer()
server.RegisterBuild(build) RegisterBuild(server, build)
server.RegisterUi(ui) RegisterUi(server, ui)
server.Start()
defer server.Stop()
args := &BuilderRunArgs{server.Address()} args := &BuilderRunArgs{serveSingleConn(server)}
b.client.Call("Builder.Run", args, new(interface{})) b.client.Call("Builder.Run", args, new(interface{}))
} }
......
...@@ -33,13 +33,12 @@ func TestBuilderRPC(t *testing.T) { ...@@ -33,13 +33,12 @@ func TestBuilderRPC(t *testing.T) {
b := new(testBuilder) b := new(testBuilder)
// Start the server // Start the server
server := NewServer() server := rpc.NewServer()
server.RegisterBuilder(b) RegisterBuilder(server, b)
server.Start() address := serveSingleConn(server)
defer server.Stop()
// Create the client over RPC and run some methods to verify it works // Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", server.Address()) client, err := rpc.Dial("tcp", address)
assert.Nil(err, "should be able to connect") assert.Nil(err, "should be able to connect")
// Test Prepare // Test Prepare
......
...@@ -49,11 +49,10 @@ func (e *EnvironmentServer) Builder(name *string, reply *string) error { ...@@ -49,11 +49,10 @@ func (e *EnvironmentServer) Builder(name *string, reply *string) error {
builder := e.env.Builder(*name) builder := e.env.Builder(*name)
// Wrap it // Wrap it
server := NewServer() server := rpc.NewServer()
server.RegisterBuilder(builder) RegisterBuilder(server, builder)
server.StartSingle()
*reply = server.Address() *reply = serveSingleConn(server)
return nil return nil
} }
...@@ -66,10 +65,9 @@ func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error { ...@@ -66,10 +65,9 @@ func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error {
ui := e.env.Ui() ui := e.env.Ui()
// Wrap it // Wrap it
server := NewServer() server := rpc.NewServer()
server.RegisterUi(ui) RegisterUi(server, ui)
server.StartSingle()
*reply = server.Address() *reply = serveSingleConn(server)
return nil return nil
} }
...@@ -42,13 +42,12 @@ func TestEnvironmentRPC(t *testing.T) { ...@@ -42,13 +42,12 @@ func TestEnvironmentRPC(t *testing.T) {
e := &testEnvironment{} e := &testEnvironment{}
// Start the server // Start the server
server := NewServer() server := rpc.NewServer()
server.RegisterEnvironment(e) RegisterEnvironment(server, e)
server.Start() address := serveSingleConn(server)
defer server.Stop()
// Create the client over RPC and run some methods to verify it works // Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", server.Address()) client, err := rpc.Dial("tcp", address)
assert.Nil(err, "should be able to connect") assert.Nil(err, "should be able to connect")
eClient := &Environment{client} eClient := &Environment{client}
......
...@@ -16,16 +16,18 @@ func Test_netListenerInRange(t *testing.T) { ...@@ -16,16 +16,18 @@ func Test_netListenerInRange(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
// Open up port 10000 so that we take up a port // Open up port 10000 so that we take up a port
L1000, err := net.Listen("tcp", ":10000") L1000, err := net.Listen("tcp", ":11000")
defer L1000.Close() defer L1000.Close()
assert.Nil(err, "should be able to bind to port 10000") assert.Nil(err, "should be able to bind to port 10000")
// Verify it selects an open port if err == nil {
L := netListenerInRange(10000, 10005) // Verify it selects an open port
assert.NotNil(L, "should have a listener") L := netListenerInRange(11000, 11005)
assert.Equal(addrPort(L.Addr()), "10001", "should bind to open port") assert.NotNil(L, "should have a listener")
assert.Equal(addrPort(L.Addr()), "11001", "should bind to open port")
// Returns nil if there are no open ports // Returns nil if there are no open ports
L = netListenerInRange(10000, 10000) L = netListenerInRange(11000, 11000)
assert.Nil(L, "should not get a listener") assert.Nil(L, "should not get a listener")
}
} }
package rpc package rpc
import ( import (
"errors"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"net"
"net/rpc" "net/rpc"
) )
func RegisterCommand(s *rpc.Server, c packer.Command) { // Registers the appropriate endpoint on an RPC server to serve a
s.RegisterName("Command", &ServerCommand{c}) // Packer Build.
} func RegisterBuild(s *rpc.Server, b packer.Build) {
s.RegisterName("Build", &BuildServer{b})
// A Server is a Golang RPC server that has helper methods for automatically
// setting up the endpoints for Packer interfaces.
type Server struct {
listener net.Listener
server *rpc.Server
}
// Creates and returns a new Server.
func NewServer() *Server {
return &Server{
server: rpc.NewServer(),
}
} }
func (s *Server) Address() string { // Registers the appropriate endpoint on an RPC server to serve a
if s.listener == nil { // Packer Builder.
panic("Server not listening.") func RegisterBuilder(s *rpc.Server, b packer.Builder) {
} s.RegisterName("Builder", &BuilderServer{b})
return s.listener.Addr().String()
}
func (s *Server) RegisterBuild(b packer.Build) {
s.server.RegisterName("Build", &BuildServer{b})
} }
func (s *Server) RegisterBuilder(b packer.Builder) { // Registers the appropriate endpoint on an RPC server to serve a
s.server.RegisterName("Builder", &BuilderServer{b}) // Packer Command.
} func RegisterCommand(s *rpc.Server, c packer.Command) {
s.RegisterName("Command", &ServerCommand{c})
func (s *Server) RegisterCommand(c packer.Command) {
s.server.RegisterName("Command", &ServerCommand{c})
}
func (s *Server) RegisterEnvironment(e packer.Environment) {
s.server.RegisterName("Environment", &EnvironmentServer{e})
}
func (s *Server) RegisterUi(ui packer.Ui) {
s.server.RegisterName("Ui", &UiServer{ui})
}
func (s *Server) Start() error {
return s.start(false)
} }
func (s *Server) StartSingle() error { // Registers the appropriate endpoint on an RPC server to serve a
return s.start(true) // Packer Environment
func RegisterEnvironment(s *rpc.Server, e packer.Environment) {
s.RegisterName("Environment", &EnvironmentServer{e})
} }
func (s *Server) Stop() { // Registers the appropriate endpoint on an RPC server to serve a
if s.listener != nil { // Packer UI
s.listener.Close() func RegisterUi(s *rpc.Server, ui packer.Ui) {
s.listener = nil s.RegisterName("Ui", &UiServer{ui})
}
} }
func (s *Server) start(singleConn bool) error { func serveSingleConn(s *rpc.Server) string {
if s.listener != nil { l := netListenerInRange(portRangeMin, portRangeMax)
return errors.New("Server already started.")
}
// Start the TCP listener and a goroutine responsible for cleaning up the
// listener.
s.listener = netListenerInRange(portRangeMin, portRangeMax)
if s.listener == nil {
return errors.New("Could not open a port ot listen on.")
}
// Start accepting connections
go func(l net.Listener) {
for {
conn, err := l.Accept()
if err != nil {
break
}
go s.server.ServeConn(conn) // Accept a single connection in a goroutine and then exit
go func() {
// If we're only accepting a single connection then defer l.Close()
// stop. conn, err := l.Accept()
if singleConn { if err != nil {
s.Stop() return
break
}
} }
}(s.listener)
return nil s.ServeConn(conn)
}()
return l.Addr().String()
} }
package rpc package rpc
import (
"cgl.tideland.biz/asserts"
"net/rpc"
"testing"
)
func TestServer_Address_PanicIfNotStarted(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
defer func() {
p := recover()
assert.NotNil(p, "should panic")
assert.Equal(p.(string), "Server not listening.", "right panic")
}()
NewServer().Address()
}
func TestServer_Start(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
s := NewServer()
// Verify it can start
err := s.Start()
assert.Nil(err, "should start without err")
addr := s.Address()
// Verify we can connect to it!
_, err = rpc.Dial("tcp", addr)
assert.Nil(err, "should be able to connect to RPC")
// Verify it stops
s.Stop()
_, err = rpc.Dial("tcp", addr)
assert.NotNil(err, "should NOT be able to connect to RPC")
}
func TestServer_RegisterUi(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
ui := &testUi{}
// Start the server with a UI
s := NewServer()
s.RegisterUi(ui)
assert.Nil(s.Start(), "should start properly")
defer s.Stop()
// Verify it works
client, err := rpc.Dial("tcp", s.Address())
assert.Nil(err, "should connect via RPC")
uiClient := &Ui{client}
uiClient.Say("format")
assert.Equal(ui.sayFormat, "format", "format should be correct")
}
...@@ -25,13 +25,12 @@ func TestUiRPC(t *testing.T) { ...@@ -25,13 +25,12 @@ func TestUiRPC(t *testing.T) {
ui := new(testUi) ui := new(testUi)
// Start the RPC server // Start the RPC server
server := NewServer() server := rpc.NewServer()
server.RegisterUi(ui) RegisterUi(server, ui)
server.Start() address := serveSingleConn(server)
defer server.Stop()
// Create the client over RPC and run some methods to verify it works // Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", server.Address()) client, err := rpc.Dial("tcp", address)
if err != nil { if err != nil {
panic(err) panic(err)
} }
......
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