diff --git a/rpc/error.go b/rpc/error.go new file mode 100644 index 000000000..c3ab7b1a4 --- /dev/null +++ b/rpc/error.go @@ -0,0 +1,21 @@ +package rpc + +// This is a type that wraps error types so that they can be messaged +// across RPC channels. Since "error" is an interface, we can't always +// gob-encode the underlying structure. This is a valid error interface +// implementer that we will push across. +type BasicError struct { + Message string +} + +func NewBasicError(err error) *BasicError { + if err == nil { + return nil + } + + return &BasicError{err.Error()} +} + +func (e *BasicError) Error() string { + return e.Message +} diff --git a/rpc/error_test.go b/rpc/error_test.go new file mode 100644 index 000000000..8ca8b60eb --- /dev/null +++ b/rpc/error_test.go @@ -0,0 +1,26 @@ +package rpc + +import ( + "errors" + "testing" +) + +func TestBasicError_ImplementsError(t *testing.T) { + var _ error = new(BasicError) +} + +func TestBasicError_MatchesMessage(t *testing.T) { + err := errors.New("foo") + wrapped := NewBasicError(err) + + if wrapped.Error() != err.Error() { + t.Fatalf("bad: %#v", wrapped.Error()) + } +} + +func TestNewBasicError_nil(t *testing.T) { + r := NewBasicError(nil) + if r != nil { + t.Fatalf("bad: %#v", r) + } +} diff --git a/rpc/resource_provider.go b/rpc/resource_provider.go index c1026dd05..c52a965e6 100644 --- a/rpc/resource_provider.go +++ b/rpc/resource_provider.go @@ -17,8 +17,11 @@ func (p *ResourceProvider) Configure(c map[string]interface{}) ([]string, error) if err != nil { return nil, err } + if resp.Error != nil { + err = resp.Error + } - return resp.Warnings, resp.Error + return resp.Warnings, err } type ResourceProviderServer struct { @@ -27,7 +30,7 @@ type ResourceProviderServer struct { type ResourceProviderConfigureResponse struct { Warnings []string - Error error + Error *BasicError } func (s *ResourceProviderServer) Configure( @@ -36,7 +39,7 @@ func (s *ResourceProviderServer) Configure( warnings, err := s.Provider.Configure(config) *reply = ResourceProviderConfigureResponse{ Warnings: warnings, - Error: err, + Error: NewBasicError(err), } return nil } diff --git a/rpc/resource_provider_test.go b/rpc/resource_provider_test.go index 624255983..9e2932ba7 100644 --- a/rpc/resource_provider_test.go +++ b/rpc/resource_provider_test.go @@ -1,6 +1,7 @@ package rpc import ( + "errors" "reflect" "testing" @@ -32,3 +33,65 @@ func TestResourceProvider_configure(t *testing.T) { t.Fatalf("bad: %#v", e) } } + +func TestResourceProvider_configure_errors(t *testing.T) { + p := new(terraform.MockResourceProvider) + client, server := testClientServer(t) + name, err := Register(server, p) + if err != nil { + t.Fatalf("err: %s", err) + } + provider := &ResourceProvider{Client: client, Name: name} + + p.ConfigureReturnError = errors.New("foo") + + // Configure + config := map[string]interface{}{"foo": "bar"} + w, e := provider.Configure(config) + if !p.ConfigureCalled { + t.Fatal("configure should be called") + } + if !reflect.DeepEqual(p.ConfigureConfig, config) { + t.Fatalf("bad: %#v", p.ConfigureConfig) + } + if w != nil { + t.Fatalf("bad: %#v", w) + } + if e == nil { + t.Fatal("should have error") + } + if e.Error() != "foo" { + t.Fatalf("bad: %s", e) + } +} + +func TestResourceProvider_configure_warnings(t *testing.T) { + p := new(terraform.MockResourceProvider) + client, server := testClientServer(t) + name, err := Register(server, p) + if err != nil { + t.Fatalf("err: %s", err) + } + provider := &ResourceProvider{Client: client, Name: name} + + p.ConfigureReturnWarnings = []string{"foo", "bar"} + + // Configure + config := map[string]interface{}{"foo": "bar"} + w, e := provider.Configure(config) + if !p.ConfigureCalled { + t.Fatal("configure should be called") + } + if !reflect.DeepEqual(p.ConfigureConfig, config) { + t.Fatalf("bad: %#v", p.ConfigureConfig) + } + if w == nil { + t.Fatal("should have warnings") + } + if !reflect.DeepEqual(w, []string{"foo", "bar"}) { + t.Fatalf("bad: %#v", w) + } + if e != nil { + t.Fatalf("bad: %#v", e) + } +}