rpc: Speak new API with UIInput
This commit is contained in:
parent
5611b9b8a8
commit
caf8e372f2
|
@ -82,6 +82,7 @@ func (c *Client) ResourceProvider() (terraform.ResourceProvider, error) {
|
|||
}
|
||||
|
||||
return &ResourceProvider{
|
||||
Broker: c.broker,
|
||||
Client: rpc.NewClient(conn),
|
||||
Name: "ResourceProvider",
|
||||
}, nil
|
||||
|
|
|
@ -9,10 +9,37 @@ import (
|
|||
// ResourceProvider is an implementation of terraform.ResourceProvider
|
||||
// that communicates over RPC.
|
||||
type ResourceProvider struct {
|
||||
Broker *muxBroker
|
||||
Client *rpc.Client
|
||||
Name string
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Input(
|
||||
input terraform.UIInput,
|
||||
c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
||||
id := p.Broker.NextId()
|
||||
go acceptAndServe(p.Broker, id, "UIInput", &UIInputServer{
|
||||
UIInput: input,
|
||||
})
|
||||
|
||||
var resp ResourceProviderInputResponse
|
||||
args := ResourceProviderInputArgs{
|
||||
InputId: id,
|
||||
Config: c,
|
||||
}
|
||||
|
||||
err := p.Client.Call(p.Name+".Input", &args, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Error != nil {
|
||||
err = resp.Error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Config, nil
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||
var resp ResourceProviderValidateResponse
|
||||
args := ResourceProviderValidateArgs{
|
||||
|
@ -150,6 +177,7 @@ func (p *ResourceProvider) Resources() []terraform.ResourceType {
|
|||
// ResourceProviderServer is a net/rpc compatible structure for serving
|
||||
// a ResourceProvider. This should not be used directly.
|
||||
type ResourceProviderServer struct {
|
||||
Broker *muxBroker
|
||||
Provider terraform.ResourceProvider
|
||||
}
|
||||
|
||||
|
@ -157,6 +185,16 @@ type ResourceProviderConfigureResponse struct {
|
|||
Error *BasicError
|
||||
}
|
||||
|
||||
type ResourceProviderInputArgs struct {
|
||||
InputId uint32
|
||||
Config *terraform.ResourceConfig
|
||||
}
|
||||
|
||||
type ResourceProviderInputResponse struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Error *BasicError
|
||||
}
|
||||
|
||||
type ResourceProviderApplyArgs struct {
|
||||
Info *terraform.InstanceInfo
|
||||
State *terraform.InstanceState
|
||||
|
@ -208,6 +246,33 @@ type ResourceProviderValidateResourceResponse struct {
|
|||
Errors []*BasicError
|
||||
}
|
||||
|
||||
func (s *ResourceProviderServer) Input(
|
||||
args *ResourceProviderInputArgs,
|
||||
reply *ResourceProviderInputResponse) error {
|
||||
conn, err := s.Broker.Dial(args.InputId)
|
||||
if err != nil {
|
||||
*reply = ResourceProviderInputResponse{
|
||||
Error: NewBasicError(err),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
client := rpc.NewClient(conn)
|
||||
defer client.Close()
|
||||
|
||||
input := &UIInput{
|
||||
Client: client,
|
||||
Name: "UIInput",
|
||||
}
|
||||
|
||||
config, err := s.Provider.Input(input, args.Config)
|
||||
*reply = ResourceProviderInputResponse{
|
||||
Config: config,
|
||||
Error: NewBasicError(err),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ResourceProviderServer) Validate(
|
||||
args *ResourceProviderValidateArgs,
|
||||
reply *ResourceProviderValidateResponse) error {
|
||||
|
|
|
@ -12,14 +12,54 @@ func TestResourceProvider_impl(t *testing.T) {
|
|||
var _ terraform.ResourceProvider = new(ResourceProvider)
|
||||
}
|
||||
|
||||
func TestResourceProvider_configure(t *testing.T) {
|
||||
p := new(terraform.MockResourceProvider)
|
||||
client, server := testClientServer(t)
|
||||
name, err := Register(server, p)
|
||||
func TestResourceProvider_input(t *testing.T) {
|
||||
client, server := testNewClientServer(t)
|
||||
defer client.Close()
|
||||
|
||||
p := server.ProviderFunc().(*terraform.MockResourceProvider)
|
||||
|
||||
provider, err := client.ResourceProvider()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
input := new(terraform.MockUIInput)
|
||||
|
||||
expected := &terraform.ResourceConfig{
|
||||
Raw: map[string]interface{}{"bar": "baz"},
|
||||
}
|
||||
p.InputReturnConfig = expected
|
||||
|
||||
// Input
|
||||
config := &terraform.ResourceConfig{
|
||||
Raw: map[string]interface{}{"foo": "bar"},
|
||||
}
|
||||
actual, err := provider.Input(input, config)
|
||||
if !p.InputCalled {
|
||||
t.Fatal("input should be called")
|
||||
}
|
||||
if !reflect.DeepEqual(p.InputConfig, config) {
|
||||
t.Fatalf("bad: %#v", p.InputConfig)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_configure(t *testing.T) {
|
||||
client, server := testNewClientServer(t)
|
||||
defer client.Close()
|
||||
|
||||
p := server.ProviderFunc().(*terraform.MockResourceProvider)
|
||||
|
||||
provider, err := client.ResourceProvider()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
provider := &ResourceProvider{Client: client, Name: name}
|
||||
|
||||
// Configure
|
||||
config := &terraform.ResourceConfig{
|
||||
|
|
|
@ -46,6 +46,24 @@ func testClientServer(t *testing.T) (*rpc.Client, *rpc.Server) {
|
|||
return client, server
|
||||
}
|
||||
|
||||
func testNewClientServer(t *testing.T) (*Client, *Server) {
|
||||
clientConn, serverConn := testConn(t)
|
||||
|
||||
server := &Server{
|
||||
ProviderFunc: testProviderFixed(new(terraform.MockResourceProvider)),
|
||||
ProvisionerFunc: testProvisionerFixed(
|
||||
new(terraform.MockResourceProvisioner)),
|
||||
}
|
||||
go server.ServeConn(serverConn)
|
||||
|
||||
client, err := NewClient(clientConn)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return client, server
|
||||
}
|
||||
|
||||
func testProviderFixed(p terraform.ResourceProvider) ProviderFunc {
|
||||
return func() terraform.ResourceProvider {
|
||||
return p
|
||||
|
|
|
@ -96,7 +96,8 @@ func (d *dispenseServer) ResourceProvider(
|
|||
return
|
||||
}
|
||||
|
||||
d.serve(conn, "ResourceProvider", &ResourceProviderServer{
|
||||
serve(conn, "ResourceProvider", &ResourceProviderServer{
|
||||
Broker: d.broker,
|
||||
Provider: d.ProviderFunc(),
|
||||
})
|
||||
}()
|
||||
|
@ -116,7 +117,7 @@ func (d *dispenseServer) ResourceProvisioner(
|
|||
return
|
||||
}
|
||||
|
||||
d.serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
||||
serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
||||
Provisioner: d.ProvisionerFunc(),
|
||||
})
|
||||
}()
|
||||
|
@ -124,7 +125,17 @@ func (d *dispenseServer) ResourceProvisioner(
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *dispenseServer) serve(conn io.ReadWriteCloser, name string, v interface{}) {
|
||||
func acceptAndServe(mux *muxBroker, id uint32, n string, v interface{}) {
|
||||
conn, err := mux.Accept(id)
|
||||
if err != nil {
|
||||
log.Printf("[ERR] Plugin acceptAndServe: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
serve(conn, n, v)
|
||||
}
|
||||
|
||||
func serve(conn io.ReadWriteCloser, name string, v interface{}) {
|
||||
server := rpc.NewServer()
|
||||
if err := server.RegisterName(name, v); err != nil {
|
||||
log.Printf("[ERR] Plugin dispense: %s", err)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"net/rpc"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// UIInput is an implementatin of terraform.UIInput that communicates
|
||||
// over RPC.
|
||||
type UIInput struct {
|
||||
Client *rpc.Client
|
||||
Name string
|
||||
}
|
||||
|
||||
func (i *UIInput) Input(opts *terraform.InputOpts) (string, error) {
|
||||
var resp UIInputInputResponse
|
||||
err := i.Client.Call(i.Name+".Input", opts, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.Error != nil {
|
||||
err = resp.Error
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.Value, nil
|
||||
}
|
||||
|
||||
type UIInputInputResponse struct {
|
||||
Value string
|
||||
Error *BasicError
|
||||
}
|
||||
|
||||
// UIInputServer is a net/rpc compatible structure for serving
|
||||
// a UIInputServer. This should not be used directly.
|
||||
type UIInputServer struct {
|
||||
UIInput terraform.UIInput
|
||||
}
|
||||
|
||||
func (s *UIInputServer) Input(
|
||||
opts *terraform.InputOpts,
|
||||
reply *UIInputInputResponse) error {
|
||||
value, err := s.UIInput.Input(opts)
|
||||
*reply = UIInputInputResponse{
|
||||
Value: value,
|
||||
Error: NewBasicError(err),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestUIInput_impl(t *testing.T) {
|
||||
var _ terraform.UIInput = new(UIInput)
|
||||
}
|
||||
|
||||
func TestUIInput_input(t *testing.T) {
|
||||
client, server := testClientServer(t)
|
||||
defer client.Close()
|
||||
|
||||
i := new(terraform.MockUIInput)
|
||||
i.InputReturnString = "foo"
|
||||
|
||||
err := server.RegisterName("UIInput", &UIInputServer{
|
||||
UIInput: i,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
input := &UIInput{Client: client, Name: "UIInput"}
|
||||
|
||||
opts := &terraform.InputOpts{
|
||||
Id: "foo",
|
||||
}
|
||||
|
||||
v, err := input.Input(opts)
|
||||
if !i.InputCalled {
|
||||
t.Fatal("input should be called")
|
||||
}
|
||||
if !reflect.DeepEqual(i.InputOpts, opts) {
|
||||
t.Fatalf("bad: %#v", i.InputOpts)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
|
||||
if v != "foo" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue