Merge pull request #361 from hashicorp/f-provisioner-ui
Foundational support for UI output from provisioners
This commit is contained in:
commit
28acb7baa8
|
@ -13,7 +13,9 @@ import (
|
||||||
|
|
||||||
type ResourceProvisioner struct{}
|
type ResourceProvisioner struct{}
|
||||||
|
|
||||||
func (p *ResourceProvisioner) Apply(s *terraform.InstanceState,
|
func (p *ResourceProvisioner) Apply(
|
||||||
|
o terraform.UIOutput,
|
||||||
|
s *terraform.InstanceState,
|
||||||
c *terraform.ResourceConfig) error {
|
c *terraform.ResourceConfig) error {
|
||||||
// Ensure the connection type is SSH
|
// Ensure the connection type is SSH
|
||||||
if err := helper.VerifySSH(s); err != nil {
|
if err := helper.VerifySSH(s); err != nil {
|
||||||
|
|
|
@ -20,6 +20,7 @@ const (
|
||||||
type ResourceProvisioner struct{}
|
type ResourceProvisioner struct{}
|
||||||
|
|
||||||
func (p *ResourceProvisioner) Apply(
|
func (p *ResourceProvisioner) Apply(
|
||||||
|
o terraform.UIOutput,
|
||||||
s *terraform.InstanceState,
|
s *terraform.InstanceState,
|
||||||
c *terraform.ResourceConfig) error {
|
c *terraform.ResourceConfig) error {
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,9 @@ func TestResourceProvider_Apply(t *testing.T) {
|
||||||
"command": "echo foo > test_out",
|
"command": "echo foo > test_out",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
output := new(terraform.MockUIOutput)
|
||||||
p := new(ResourceProvisioner)
|
p := new(ResourceProvisioner)
|
||||||
if err := p.Apply(nil, c); err != nil {
|
if err := p.Apply(output, nil, c); err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,9 @@ const (
|
||||||
|
|
||||||
type ResourceProvisioner struct{}
|
type ResourceProvisioner struct{}
|
||||||
|
|
||||||
func (p *ResourceProvisioner) Apply(s *terraform.InstanceState,
|
func (p *ResourceProvisioner) Apply(
|
||||||
|
o terraform.UIOutput,
|
||||||
|
s *terraform.InstanceState,
|
||||||
c *terraform.ResourceConfig) error {
|
c *terraform.ResourceConfig) error {
|
||||||
// Ensure the connection type is SSH
|
// Ensure the connection type is SSH
|
||||||
if err := helper.VerifySSH(s); err != nil {
|
if err := helper.VerifySSH(s); err != nil {
|
||||||
|
|
|
@ -164,6 +164,18 @@ func (h *UiHook) PreProvision(
|
||||||
return terraform.HookActionContinue, nil
|
return terraform.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *UiHook) ProvisionOutput(
|
||||||
|
n *terraform.InstanceInfo,
|
||||||
|
provId string,
|
||||||
|
msg string) {
|
||||||
|
id := n.HumanId()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(h.Colorize.Color(fmt.Sprintf(
|
||||||
|
"[reset]%s (%s): ", id, provId)))
|
||||||
|
buf.WriteString(msg)
|
||||||
|
h.ui.Output(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
func (h *UiHook) PreRefresh(
|
func (h *UiHook) PreRefresh(
|
||||||
n *terraform.InstanceInfo,
|
n *terraform.InstanceInfo,
|
||||||
s *terraform.InstanceState) (terraform.HookAction, error) {
|
s *terraform.InstanceState) (terraform.HookAction, error) {
|
||||||
|
|
|
@ -101,6 +101,7 @@ func (c *Client) ResourceProvisioner() (terraform.ResourceProvisioner, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ResourceProvisioner{
|
return &ResourceProvisioner{
|
||||||
|
Broker: c.broker,
|
||||||
Client: rpc.NewClient(conn),
|
Client: rpc.NewClient(conn),
|
||||||
Name: "ResourceProvisioner",
|
Name: "ResourceProvisioner",
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -60,9 +60,10 @@ func TestClient_ResourceProvisioner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply
|
// Apply
|
||||||
|
output := &terraform.MockUIOutput{}
|
||||||
state := &terraform.InstanceState{}
|
state := &terraform.InstanceState{}
|
||||||
conf := &terraform.ResourceConfig{}
|
conf := &terraform.ResourceConfig{}
|
||||||
err = provisioner.Apply(state, conf)
|
err = provisioner.Apply(output, state, conf)
|
||||||
if !p.ApplyCalled {
|
if !p.ApplyCalled {
|
||||||
t.Fatal("apply should be called")
|
t.Fatal("apply should be called")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
// ResourceProvisioner is an implementation of terraform.ResourceProvisioner
|
// ResourceProvisioner is an implementation of terraform.ResourceProvisioner
|
||||||
// that communicates over RPC.
|
// that communicates over RPC.
|
||||||
type ResourceProvisioner struct {
|
type ResourceProvisioner struct {
|
||||||
|
Broker *muxBroker
|
||||||
Client *rpc.Client
|
Client *rpc.Client
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
@ -36,10 +37,17 @@ func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) ([]string, [
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ResourceProvisioner) Apply(
|
func (p *ResourceProvisioner) Apply(
|
||||||
|
output terraform.UIOutput,
|
||||||
s *terraform.InstanceState,
|
s *terraform.InstanceState,
|
||||||
c *terraform.ResourceConfig) error {
|
c *terraform.ResourceConfig) error {
|
||||||
|
id := p.Broker.NextId()
|
||||||
|
go acceptAndServe(p.Broker, id, "UIOutput", &UIOutputServer{
|
||||||
|
UIOutput: output,
|
||||||
|
})
|
||||||
|
|
||||||
var resp ResourceProvisionerApplyResponse
|
var resp ResourceProvisionerApplyResponse
|
||||||
args := &ResourceProvisionerApplyArgs{
|
args := &ResourceProvisionerApplyArgs{
|
||||||
|
OutputId: id,
|
||||||
State: s,
|
State: s,
|
||||||
Config: c,
|
Config: c,
|
||||||
}
|
}
|
||||||
|
@ -65,6 +73,7 @@ type ResourceProvisionerValidateResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceProvisionerApplyArgs struct {
|
type ResourceProvisionerApplyArgs struct {
|
||||||
|
OutputId uint32
|
||||||
State *terraform.InstanceState
|
State *terraform.InstanceState
|
||||||
Config *terraform.ResourceConfig
|
Config *terraform.ResourceConfig
|
||||||
}
|
}
|
||||||
|
@ -76,13 +85,29 @@ type ResourceProvisionerApplyResponse struct {
|
||||||
// ResourceProvisionerServer is a net/rpc compatible structure for serving
|
// ResourceProvisionerServer is a net/rpc compatible structure for serving
|
||||||
// a ResourceProvisioner. This should not be used directly.
|
// a ResourceProvisioner. This should not be used directly.
|
||||||
type ResourceProvisionerServer struct {
|
type ResourceProvisionerServer struct {
|
||||||
|
Broker *muxBroker
|
||||||
Provisioner terraform.ResourceProvisioner
|
Provisioner terraform.ResourceProvisioner
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ResourceProvisionerServer) Apply(
|
func (s *ResourceProvisionerServer) Apply(
|
||||||
args *ResourceProvisionerApplyArgs,
|
args *ResourceProvisionerApplyArgs,
|
||||||
result *ResourceProvisionerApplyResponse) error {
|
result *ResourceProvisionerApplyResponse) error {
|
||||||
err := s.Provisioner.Apply(args.State, args.Config)
|
conn, err := s.Broker.Dial(args.OutputId)
|
||||||
|
if err != nil {
|
||||||
|
*result = ResourceProvisionerApplyResponse{
|
||||||
|
Error: NewBasicError(err),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
client := rpc.NewClient(conn)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
output := &UIOutput{
|
||||||
|
Client: client,
|
||||||
|
Name: "UIOutput",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Provisioner.Apply(output, args.State, args.Config)
|
||||||
*result = ResourceProvisionerApplyResponse{
|
*result = ResourceProvisionerApplyResponse{
|
||||||
Error: NewBasicError(err),
|
Error: NewBasicError(err),
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,21 @@ func TestResourceProvisioner_impl(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceProvisioner_apply(t *testing.T) {
|
func TestResourceProvisioner_apply(t *testing.T) {
|
||||||
p := new(terraform.MockResourceProvisioner)
|
client, server := testNewClientServer(t)
|
||||||
client, server := testClientServer(t)
|
defer client.Close()
|
||||||
name, err := Register(server, p)
|
|
||||||
|
p := server.ProvisionerFunc().(*terraform.MockResourceProvisioner)
|
||||||
|
|
||||||
|
provisioner, err := client.ResourceProvisioner()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
provisioner := &ResourceProvisioner{Client: client, Name: name}
|
|
||||||
|
|
||||||
// Apply
|
// Apply
|
||||||
|
output := &terraform.MockUIOutput{}
|
||||||
state := &terraform.InstanceState{}
|
state := &terraform.InstanceState{}
|
||||||
conf := &terraform.ResourceConfig{}
|
conf := &terraform.ResourceConfig{}
|
||||||
err = provisioner.Apply(state, conf)
|
err = provisioner.Apply(output, state, conf)
|
||||||
if !p.ApplyCalled {
|
if !p.ApplyCalled {
|
||||||
t.Fatal("apply should be called")
|
t.Fatal("apply should be called")
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ func (d *dispenseServer) ResourceProvisioner(
|
||||||
}
|
}
|
||||||
|
|
||||||
serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
serve(conn, "ResourceProvisioner", &ResourceProvisionerServer{
|
||||||
|
Broker: d.broker,
|
||||||
Provisioner: d.ProvisionerFunc(),
|
Provisioner: d.ProvisionerFunc(),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UIOutput is an implementatin of terraform.UIOutput that communicates
|
||||||
|
// over RPC.
|
||||||
|
type UIOutput struct {
|
||||||
|
Client *rpc.Client
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *UIOutput) Output(v string) {
|
||||||
|
o.Client.Call(o.Name+".Output", v, new(interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UIOutputServer is the RPC server for serving UIOutput.
|
||||||
|
type UIOutputServer struct {
|
||||||
|
UIOutput terraform.UIOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UIOutputServer) Output(
|
||||||
|
v string,
|
||||||
|
reply *interface{}) error {
|
||||||
|
s.UIOutput.Output(v)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUIOutput_impl(t *testing.T) {
|
||||||
|
var _ terraform.UIOutput = new(UIOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUIOutput_input(t *testing.T) {
|
||||||
|
client, server := testClientServer(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
o := new(terraform.MockUIOutput)
|
||||||
|
|
||||||
|
err := server.RegisterName("UIOutput", &UIOutputServer{
|
||||||
|
UIOutput: o,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &UIOutput{Client: client, Name: "UIOutput"}
|
||||||
|
output.Output("foo")
|
||||||
|
if !o.OutputCalled {
|
||||||
|
t.Fatal("output should be called")
|
||||||
|
}
|
||||||
|
if o.OutputMessage != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", o.OutputMessage)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1309,7 +1309,13 @@ func (c *walkContext) applyProvisioners(r *Resource, is *InstanceState) error {
|
||||||
handleHook(h.PreProvision(r.Info, prov.Type))
|
handleHook(h.PreProvision(r.Info, prov.Type))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := prov.Provisioner.Apply(is, prov.Config); err != nil {
|
output := ProvisionerUIOutput{
|
||||||
|
Info: r.Info,
|
||||||
|
Type: prov.Type,
|
||||||
|
Hooks: c.Context.hooks,
|
||||||
|
}
|
||||||
|
err := prov.Provisioner.Apply(&output, is, prov.Config)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,17 @@ type Hook interface {
|
||||||
PostDiff(*InstanceInfo, *InstanceDiff) (HookAction, error)
|
PostDiff(*InstanceInfo, *InstanceDiff) (HookAction, error)
|
||||||
|
|
||||||
// Provisioning hooks
|
// Provisioning hooks
|
||||||
|
//
|
||||||
|
// All should be self-explanatory. ProvisionOutput is called with
|
||||||
|
// output sent back by the provisioners. This will be called multiple
|
||||||
|
// times as output comes in, but each call should represent a line of
|
||||||
|
// output. The ProvisionOutput method cannot control whether the
|
||||||
|
// hook continues running.
|
||||||
PreProvisionResource(*InstanceInfo, *InstanceState) (HookAction, error)
|
PreProvisionResource(*InstanceInfo, *InstanceState) (HookAction, error)
|
||||||
PostProvisionResource(*InstanceInfo, *InstanceState) (HookAction, error)
|
PostProvisionResource(*InstanceInfo, *InstanceState) (HookAction, error)
|
||||||
PreProvision(*InstanceInfo, string) (HookAction, error)
|
PreProvision(*InstanceInfo, string) (HookAction, error)
|
||||||
PostProvision(*InstanceInfo, string) (HookAction, error)
|
PostProvision(*InstanceInfo, string) (HookAction, error)
|
||||||
|
ProvisionOutput(*InstanceInfo, string, string)
|
||||||
|
|
||||||
// PreRefresh and PostRefresh are called before and after a single
|
// PreRefresh and PostRefresh are called before and after a single
|
||||||
// resource state is refreshed, respectively.
|
// resource state is refreshed, respectively.
|
||||||
|
@ -81,6 +88,10 @@ func (*NilHook) PostProvision(*InstanceInfo, string) (HookAction, error) {
|
||||||
return HookActionContinue, nil
|
return HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*NilHook) ProvisionOutput(
|
||||||
|
*InstanceInfo, string, string) {
|
||||||
|
}
|
||||||
|
|
||||||
func (*NilHook) PreRefresh(*InstanceInfo, *InstanceState) (HookAction, error) {
|
func (*NilHook) PreRefresh(*InstanceInfo, *InstanceState) (HookAction, error) {
|
||||||
return HookActionContinue, nil
|
return HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,11 @@ type MockHook struct {
|
||||||
PostProvisionReturn HookAction
|
PostProvisionReturn HookAction
|
||||||
PostProvisionError error
|
PostProvisionError error
|
||||||
|
|
||||||
|
ProvisionOutputCalled bool
|
||||||
|
ProvisionOutputInfo *InstanceInfo
|
||||||
|
ProvisionOutputProvisionerId string
|
||||||
|
ProvisionOutputMessage string
|
||||||
|
|
||||||
PostRefreshCalled bool
|
PostRefreshCalled bool
|
||||||
PostRefreshInfo *InstanceInfo
|
PostRefreshInfo *InstanceInfo
|
||||||
PostRefreshState *InstanceState
|
PostRefreshState *InstanceState
|
||||||
|
@ -124,6 +129,16 @@ func (h *MockHook) PostProvision(n *InstanceInfo, provId string) (HookAction, er
|
||||||
return h.PostProvisionReturn, h.PostProvisionError
|
return h.PostProvisionReturn, h.PostProvisionError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *MockHook) ProvisionOutput(
|
||||||
|
n *InstanceInfo,
|
||||||
|
provId string,
|
||||||
|
msg string) {
|
||||||
|
h.ProvisionOutputCalled = true
|
||||||
|
h.ProvisionOutputInfo = n
|
||||||
|
h.ProvisionOutputProvisionerId = provId
|
||||||
|
h.ProvisionOutputMessage = msg
|
||||||
|
}
|
||||||
|
|
||||||
func (h *MockHook) PreRefresh(n *InstanceInfo, s *InstanceState) (HookAction, error) {
|
func (h *MockHook) PreRefresh(n *InstanceInfo, s *InstanceState) (HookAction, error) {
|
||||||
h.PreRefreshCalled = true
|
h.PreRefreshCalled = true
|
||||||
h.PreRefreshInfo = n
|
h.PreRefreshInfo = n
|
||||||
|
|
|
@ -42,6 +42,9 @@ func (h *stopHook) PostProvision(*InstanceInfo, string) (HookAction, error) {
|
||||||
return h.hook()
|
return h.hook()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *stopHook) ProvisionOutput(*InstanceInfo, string, string) {
|
||||||
|
}
|
||||||
|
|
||||||
func (h *stopHook) PreRefresh(*InstanceInfo, *InstanceState) (HookAction, error) {
|
func (h *stopHook) PreRefresh(*InstanceInfo, *InstanceState) (HookAction, error) {
|
||||||
return h.hook()
|
return h.hook()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ type ResourceProvisioner interface {
|
||||||
// resource state along with an error. Instead of a diff, the ResourceConfig
|
// resource state along with an error. Instead of a diff, the ResourceConfig
|
||||||
// is provided since provisioners only run after a resource has been
|
// is provided since provisioners only run after a resource has been
|
||||||
// newly created.
|
// newly created.
|
||||||
Apply(*InstanceState, *ResourceConfig) error
|
Apply(UIOutput, *InstanceState, *ResourceConfig) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceProvisionerFactory is a function type that creates a new instance
|
// ResourceProvisionerFactory is a function type that creates a new instance
|
||||||
|
|
|
@ -7,6 +7,7 @@ type MockResourceProvisioner struct {
|
||||||
Meta interface{}
|
Meta interface{}
|
||||||
|
|
||||||
ApplyCalled bool
|
ApplyCalled bool
|
||||||
|
ApplyOutput UIOutput
|
||||||
ApplyState *InstanceState
|
ApplyState *InstanceState
|
||||||
ApplyConfig *ResourceConfig
|
ApplyConfig *ResourceConfig
|
||||||
ApplyFn func(*InstanceState, *ResourceConfig) error
|
ApplyFn func(*InstanceState, *ResourceConfig) error
|
||||||
|
@ -28,8 +29,12 @@ func (p *MockResourceProvisioner) Validate(c *ResourceConfig) ([]string, []error
|
||||||
return p.ValidateReturnWarns, p.ValidateReturnErrors
|
return p.ValidateReturnWarns, p.ValidateReturnErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MockResourceProvisioner) Apply(state *InstanceState, c *ResourceConfig) error {
|
func (p *MockResourceProvisioner) Apply(
|
||||||
|
output UIOutput,
|
||||||
|
state *InstanceState,
|
||||||
|
c *ResourceConfig) error {
|
||||||
p.ApplyCalled = true
|
p.ApplyCalled = true
|
||||||
|
p.ApplyOutput = output
|
||||||
p.ApplyState = state
|
p.ApplyState = state
|
||||||
p.ApplyConfig = c
|
p.ApplyConfig = c
|
||||||
if p.ApplyFn != nil {
|
if p.ApplyFn != nil {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
// UIOutput is the interface that must be implemented to output
|
||||||
|
// data to the end user.
|
||||||
|
type UIOutput interface {
|
||||||
|
Output(string)
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
type CallbackUIOutput struct {
|
||||||
|
OutputFun func(string)
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
// MockUIOutput is an implementation of UIOutput that can be used for tests.
|
||||||
|
type MockUIOutput struct {
|
||||||
|
OutputCalled bool
|
||||||
|
OutputMessage string
|
||||||
|
OutputFn func(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *MockUIOutput) Output(v string) {
|
||||||
|
o.OutputCalled = true
|
||||||
|
o.OutputMessage= v
|
||||||
|
if o.OutputFn != nil {
|
||||||
|
o.OutputFn(v)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMockUIOutput(t *testing.T) {
|
||||||
|
var _ UIOutput = new(MockUIOutput)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
// ProvisionerUIOutput is an implementation of UIOutput that calls a hook
|
||||||
|
// for the output so that the hooks can handle it.
|
||||||
|
type ProvisionerUIOutput struct {
|
||||||
|
Info *InstanceInfo
|
||||||
|
Type string
|
||||||
|
Hooks []Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ProvisionerUIOutput) Output(msg string) {
|
||||||
|
for _, h := range o.Hooks {
|
||||||
|
h.ProvisionOutput(o.Info, o.Type, msg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProvisionerUIOutput_impl(t *testing.T) {
|
||||||
|
var _ UIOutput = new(ProvisionerUIOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvisionerUIOutputOutput(t *testing.T) {
|
||||||
|
hook := new(MockHook)
|
||||||
|
output := &ProvisionerUIOutput{
|
||||||
|
Info: nil,
|
||||||
|
Type: "foo",
|
||||||
|
Hooks: []Hook{hook},
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Output("bar")
|
||||||
|
|
||||||
|
if !hook.ProvisionOutputCalled {
|
||||||
|
t.Fatal("should be called")
|
||||||
|
}
|
||||||
|
if hook.ProvisionOutputProvisionerId != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", hook.ProvisionOutputProvisionerId)
|
||||||
|
}
|
||||||
|
if hook.ProvisionOutputMessage != "bar" {
|
||||||
|
t.Fatalf("bad: %#v", hook.ProvisionOutputMessage)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue