package plugin import ( "net/rpc" plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/terraform/terraform" ) // ResourceProviderPlugin is the plugin.Plugin implementation. type ResourceProviderPlugin struct { ResourceProvider func() terraform.ResourceProvider } func (p *ResourceProviderPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { return &ResourceProviderServer{ Broker: b, Provider: p.ResourceProvider(), }, nil } func (p *ResourceProviderPlugin) Client( b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { return &ResourceProvider{Broker: b, Client: c}, nil } // ResourceProvider is an implementation of terraform.ResourceProvider // that communicates over RPC. type ResourceProvider struct { Broker *plugin.MuxBroker Client *rpc.Client } func (p *ResourceProvider) Stop() error { var resp ResourceProviderStopResponse err := p.Client.Call("Plugin.Stop", new(interface{}), &resp) if err != nil { return err } if resp.Error != nil { err = resp.Error } return err } func (p *ResourceProvider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { var result ResourceProviderGetSchemaResponse args := &ResourceProviderGetSchemaArgs{ Req: req, } err := p.Client.Call("Plugin.GetSchema", args, &result) if err != nil { return nil, err } if result.Error != nil { err = result.Error } return result.Schema, err } func (p *ResourceProvider) Input( input terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { id := p.Broker.NextId() go p.Broker.AcceptAndServe(id, &UIInputServer{ UIInput: input, }) var resp ResourceProviderInputResponse args := ResourceProviderInputArgs{ InputId: id, Config: c, } err := p.Client.Call("Plugin.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{ Config: c, } err := p.Client.Call("Plugin.Validate", &args, &resp) if err != nil { return nil, []error{err} } var errs []error if len(resp.Errors) > 0 { errs = make([]error, len(resp.Errors)) for i, err := range resp.Errors { errs[i] = err } } return resp.Warnings, errs } func (p *ResourceProvider) ValidateResource( t string, c *terraform.ResourceConfig) ([]string, []error) { var resp ResourceProviderValidateResourceResponse args := ResourceProviderValidateResourceArgs{ Config: c, Type: t, } err := p.Client.Call("Plugin.ValidateResource", &args, &resp) if err != nil { return nil, []error{err} } var errs []error if len(resp.Errors) > 0 { errs = make([]error, len(resp.Errors)) for i, err := range resp.Errors { errs[i] = err } } return resp.Warnings, errs } func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error { var resp ResourceProviderConfigureResponse err := p.Client.Call("Plugin.Configure", c, &resp) if err != nil { return err } if resp.Error != nil { err = resp.Error } return err } func (p *ResourceProvider) Apply( info *terraform.InstanceInfo, s *terraform.InstanceState, d *terraform.InstanceDiff) (*terraform.InstanceState, error) { var resp ResourceProviderApplyResponse args := &ResourceProviderApplyArgs{ Info: info, State: s, Diff: d, } err := p.Client.Call("Plugin.Apply", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.State, err } func (p *ResourceProvider) Diff( info *terraform.InstanceInfo, s *terraform.InstanceState, c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { var resp ResourceProviderDiffResponse args := &ResourceProviderDiffArgs{ Info: info, State: s, Config: c, } err := p.Client.Call("Plugin.Diff", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.Diff, err } func (p *ResourceProvider) ValidateDataSource( t string, c *terraform.ResourceConfig) ([]string, []error) { var resp ResourceProviderValidateResourceResponse args := ResourceProviderValidateResourceArgs{ Config: c, Type: t, } err := p.Client.Call("Plugin.ValidateDataSource", &args, &resp) if err != nil { return nil, []error{err} } var errs []error if len(resp.Errors) > 0 { errs = make([]error, len(resp.Errors)) for i, err := range resp.Errors { errs[i] = err } } return resp.Warnings, errs } func (p *ResourceProvider) Refresh( info *terraform.InstanceInfo, s *terraform.InstanceState) (*terraform.InstanceState, error) { var resp ResourceProviderRefreshResponse args := &ResourceProviderRefreshArgs{ Info: info, State: s, } err := p.Client.Call("Plugin.Refresh", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.State, err } func (p *ResourceProvider) ImportState( info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) { var resp ResourceProviderImportStateResponse args := &ResourceProviderImportStateArgs{ Info: info, Id: id, } err := p.Client.Call("Plugin.ImportState", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.State, err } func (p *ResourceProvider) Resources() []terraform.ResourceType { var result []terraform.ResourceType err := p.Client.Call("Plugin.Resources", new(interface{}), &result) if err != nil { // TODO: panic, log, what? return nil } return result } func (p *ResourceProvider) ReadDataDiff( info *terraform.InstanceInfo, c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { var resp ResourceProviderReadDataDiffResponse args := &ResourceProviderReadDataDiffArgs{ Info: info, Config: c, } err := p.Client.Call("Plugin.ReadDataDiff", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.Diff, err } func (p *ResourceProvider) ReadDataApply( info *terraform.InstanceInfo, d *terraform.InstanceDiff) (*terraform.InstanceState, error) { var resp ResourceProviderReadDataApplyResponse args := &ResourceProviderReadDataApplyArgs{ Info: info, Diff: d, } err := p.Client.Call("Plugin.ReadDataApply", args, &resp) if err != nil { return nil, err } if resp.Error != nil { err = resp.Error } return resp.State, err } func (p *ResourceProvider) DataSources() []terraform.DataSource { var result []terraform.DataSource err := p.Client.Call("Plugin.DataSources", new(interface{}), &result) if err != nil { // TODO: panic, log, what? return nil } return result } func (p *ResourceProvider) Close() error { return p.Client.Close() } // ResourceProviderServer is a net/rpc compatible structure for serving // a ResourceProvider. This should not be used directly. type ResourceProviderServer struct { Broker *plugin.MuxBroker Provider terraform.ResourceProvider } type ResourceProviderStopResponse struct { Error *plugin.BasicError } type ResourceProviderGetSchemaArgs struct { Req *terraform.ProviderSchemaRequest } type ResourceProviderGetSchemaResponse struct { Schema *terraform.ProviderSchema Error *plugin.BasicError } type ResourceProviderConfigureResponse struct { Error *plugin.BasicError } type ResourceProviderInputArgs struct { InputId uint32 Config *terraform.ResourceConfig } type ResourceProviderInputResponse struct { Config *terraform.ResourceConfig Error *plugin.BasicError } type ResourceProviderApplyArgs struct { Info *terraform.InstanceInfo State *terraform.InstanceState Diff *terraform.InstanceDiff } type ResourceProviderApplyResponse struct { State *terraform.InstanceState Error *plugin.BasicError } type ResourceProviderDiffArgs struct { Info *terraform.InstanceInfo State *terraform.InstanceState Config *terraform.ResourceConfig } type ResourceProviderDiffResponse struct { Diff *terraform.InstanceDiff Error *plugin.BasicError } type ResourceProviderRefreshArgs struct { Info *terraform.InstanceInfo State *terraform.InstanceState } type ResourceProviderRefreshResponse struct { State *terraform.InstanceState Error *plugin.BasicError } type ResourceProviderImportStateArgs struct { Info *terraform.InstanceInfo Id string } type ResourceProviderImportStateResponse struct { State []*terraform.InstanceState Error *plugin.BasicError } type ResourceProviderReadDataApplyArgs struct { Info *terraform.InstanceInfo Diff *terraform.InstanceDiff } type ResourceProviderReadDataApplyResponse struct { State *terraform.InstanceState Error *plugin.BasicError } type ResourceProviderReadDataDiffArgs struct { Info *terraform.InstanceInfo Config *terraform.ResourceConfig } type ResourceProviderReadDataDiffResponse struct { Diff *terraform.InstanceDiff Error *plugin.BasicError } type ResourceProviderValidateArgs struct { Config *terraform.ResourceConfig } type ResourceProviderValidateResponse struct { Warnings []string Errors []*plugin.BasicError } type ResourceProviderValidateResourceArgs struct { Config *terraform.ResourceConfig Type string } type ResourceProviderValidateResourceResponse struct { Warnings []string Errors []*plugin.BasicError } func (s *ResourceProviderServer) Stop( _ interface{}, reply *ResourceProviderStopResponse) error { err := s.Provider.Stop() *reply = ResourceProviderStopResponse{ Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) GetSchema( args *ResourceProviderGetSchemaArgs, result *ResourceProviderGetSchemaResponse, ) error { schema, err := s.Provider.GetSchema(args.Req) result.Schema = schema if err != nil { result.Error = plugin.NewBasicError(err) } return nil } func (s *ResourceProviderServer) Input( args *ResourceProviderInputArgs, reply *ResourceProviderInputResponse) error { conn, err := s.Broker.Dial(args.InputId) if err != nil { *reply = ResourceProviderInputResponse{ Error: plugin.NewBasicError(err), } return nil } client := rpc.NewClient(conn) defer client.Close() input := &UIInput{Client: client} config, err := s.Provider.Input(input, args.Config) *reply = ResourceProviderInputResponse{ Config: config, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) Validate( args *ResourceProviderValidateArgs, reply *ResourceProviderValidateResponse) error { warns, errs := s.Provider.Validate(args.Config) berrs := make([]*plugin.BasicError, len(errs)) for i, err := range errs { berrs[i] = plugin.NewBasicError(err) } *reply = ResourceProviderValidateResponse{ Warnings: warns, Errors: berrs, } return nil } func (s *ResourceProviderServer) ValidateResource( args *ResourceProviderValidateResourceArgs, reply *ResourceProviderValidateResourceResponse) error { warns, errs := s.Provider.ValidateResource(args.Type, args.Config) berrs := make([]*plugin.BasicError, len(errs)) for i, err := range errs { berrs[i] = plugin.NewBasicError(err) } *reply = ResourceProviderValidateResourceResponse{ Warnings: warns, Errors: berrs, } return nil } func (s *ResourceProviderServer) Configure( config *terraform.ResourceConfig, reply *ResourceProviderConfigureResponse) error { err := s.Provider.Configure(config) *reply = ResourceProviderConfigureResponse{ Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) Apply( args *ResourceProviderApplyArgs, result *ResourceProviderApplyResponse) error { state, err := s.Provider.Apply(args.Info, args.State, args.Diff) *result = ResourceProviderApplyResponse{ State: state, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) Diff( args *ResourceProviderDiffArgs, result *ResourceProviderDiffResponse) error { diff, err := s.Provider.Diff(args.Info, args.State, args.Config) *result = ResourceProviderDiffResponse{ Diff: diff, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) Refresh( args *ResourceProviderRefreshArgs, result *ResourceProviderRefreshResponse) error { newState, err := s.Provider.Refresh(args.Info, args.State) *result = ResourceProviderRefreshResponse{ State: newState, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) ImportState( args *ResourceProviderImportStateArgs, result *ResourceProviderImportStateResponse) error { states, err := s.Provider.ImportState(args.Info, args.Id) *result = ResourceProviderImportStateResponse{ State: states, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) Resources( nothing interface{}, result *[]terraform.ResourceType) error { *result = s.Provider.Resources() return nil } func (s *ResourceProviderServer) ValidateDataSource( args *ResourceProviderValidateResourceArgs, reply *ResourceProviderValidateResourceResponse) error { warns, errs := s.Provider.ValidateDataSource(args.Type, args.Config) berrs := make([]*plugin.BasicError, len(errs)) for i, err := range errs { berrs[i] = plugin.NewBasicError(err) } *reply = ResourceProviderValidateResourceResponse{ Warnings: warns, Errors: berrs, } return nil } func (s *ResourceProviderServer) ReadDataDiff( args *ResourceProviderReadDataDiffArgs, result *ResourceProviderReadDataDiffResponse) error { diff, err := s.Provider.ReadDataDiff(args.Info, args.Config) *result = ResourceProviderReadDataDiffResponse{ Diff: diff, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) ReadDataApply( args *ResourceProviderReadDataApplyArgs, result *ResourceProviderReadDataApplyResponse) error { newState, err := s.Provider.ReadDataApply(args.Info, args.Diff) *result = ResourceProviderReadDataApplyResponse{ State: newState, Error: plugin.NewBasicError(err), } return nil } func (s *ResourceProviderServer) DataSources( nothing interface{}, result *[]terraform.DataSource) error { *result = s.Provider.DataSources() return nil }