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{
|
return &ResourceProvider{
|
||||||
|
Broker: c.broker,
|
||||||
Client: rpc.NewClient(conn),
|
Client: rpc.NewClient(conn),
|
||||||
Name: "ResourceProvider",
|
Name: "ResourceProvider",
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -9,10 +9,37 @@ import (
|
||||||
// ResourceProvider is an implementation of terraform.ResourceProvider
|
// ResourceProvider is an implementation of terraform.ResourceProvider
|
||||||
// that communicates over RPC.
|
// that communicates over RPC.
|
||||||
type ResourceProvider struct {
|
type ResourceProvider struct {
|
||||||
|
Broker *muxBroker
|
||||||
Client *rpc.Client
|
Client *rpc.Client
|
||||||
Name string
|
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) {
|
func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||||
var resp ResourceProviderValidateResponse
|
var resp ResourceProviderValidateResponse
|
||||||
args := ResourceProviderValidateArgs{
|
args := ResourceProviderValidateArgs{
|
||||||
|
@ -150,6 +177,7 @@ func (p *ResourceProvider) Resources() []terraform.ResourceType {
|
||||||
// ResourceProviderServer is a net/rpc compatible structure for serving
|
// ResourceProviderServer is a net/rpc compatible structure for serving
|
||||||
// a ResourceProvider. This should not be used directly.
|
// a ResourceProvider. This should not be used directly.
|
||||||
type ResourceProviderServer struct {
|
type ResourceProviderServer struct {
|
||||||
|
Broker *muxBroker
|
||||||
Provider terraform.ResourceProvider
|
Provider terraform.ResourceProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +185,16 @@ type ResourceProviderConfigureResponse struct {
|
||||||
Error *BasicError
|
Error *BasicError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResourceProviderInputArgs struct {
|
||||||
|
InputId uint32
|
||||||
|
Config *terraform.ResourceConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResourceProviderInputResponse struct {
|
||||||
|
Config *terraform.ResourceConfig
|
||||||
|
Error *BasicError
|
||||||
|
}
|
||||||
|
|
||||||
type ResourceProviderApplyArgs struct {
|
type ResourceProviderApplyArgs struct {
|
||||||
Info *terraform.InstanceInfo
|
Info *terraform.InstanceInfo
|
||||||
State *terraform.InstanceState
|
State *terraform.InstanceState
|
||||||
|
@ -208,6 +246,33 @@ type ResourceProviderValidateResourceResponse struct {
|
||||||
Errors []*BasicError
|
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(
|
func (s *ResourceProviderServer) Validate(
|
||||||
args *ResourceProviderValidateArgs,
|
args *ResourceProviderValidateArgs,
|
||||||
reply *ResourceProviderValidateResponse) error {
|
reply *ResourceProviderValidateResponse) error {
|
||||||
|
|
|
@ -12,14 +12,54 @@ func TestResourceProvider_impl(t *testing.T) {
|
||||||
var _ terraform.ResourceProvider = new(ResourceProvider)
|
var _ terraform.ResourceProvider = new(ResourceProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceProvider_configure(t *testing.T) {
|
func TestResourceProvider_input(t *testing.T) {
|
||||||
p := new(terraform.MockResourceProvider)
|
client, server := testNewClientServer(t)
|
||||||
client, server := testClientServer(t)
|
defer client.Close()
|
||||||
name, err := Register(server, p)
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
provider := &ResourceProvider{Client: client, Name: name}
|
|
||||||
|
|
||||||
// Configure
|
// Configure
|
||||||
config := &terraform.ResourceConfig{
|
config := &terraform.ResourceConfig{
|
||||||
|
|
|
@ -46,6 +46,24 @@ func testClientServer(t *testing.T) (*rpc.Client, *rpc.Server) {
|
||||||
return client, 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 {
|
func testProviderFixed(p terraform.ResourceProvider) ProviderFunc {
|
||||||
return func() terraform.ResourceProvider {
|
return func() terraform.ResourceProvider {
|
||||||
return p
|
return p
|
||||||
|
|
|
@ -96,7 +96,8 @@ func (d *dispenseServer) ResourceProvider(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
d.serve(conn, "ResourceProvider", &ResourceProviderServer{
|
serve(conn, "ResourceProvider", &ResourceProviderServer{
|
||||||
|
Broker: d.broker,
|
||||||
Provider: d.ProviderFunc(),
|
Provider: d.ProviderFunc(),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
@ -116,7 +117,7 @@ func (d *dispenseServer) ResourceProvisioner(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
d.serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
||||||
Provisioner: d.ProvisionerFunc(),
|
Provisioner: d.ProvisionerFunc(),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
@ -124,7 +125,17 @@ func (d *dispenseServer) ResourceProvisioner(
|
||||||
return nil
|
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()
|
server := rpc.NewServer()
|
||||||
if err := server.RegisterName(name, v); err != nil {
|
if err := server.RegisterName(name, v); err != nil {
|
||||||
log.Printf("[ERR] Plugin dispense: %s", err)
|
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