Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 56 additions & 5 deletions command/hook_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import (
"sort"
"strings"
"sync"
"time"
"unicode"

"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
)

const periodicUiTimer = 10 * time.Second

type UiHook struct {
terraform.NilHook

Expand All @@ -22,10 +25,17 @@ type UiHook struct {

l sync.Mutex
once sync.Once
resources map[string]uiResourceOp
resources map[string]uiResourceState
ui cli.Ui
}

// uiResourceState tracks the state of a single resource
type uiResourceState struct {
Op uiResourceOp
Start time.Time
}

// uiResourceOp is an enum for operations on a resource
type uiResourceOp byte

const (
Expand All @@ -51,7 +61,10 @@ func (h *UiHook) PreApply(
}

h.l.Lock()
h.resources[id] = op
h.resources[id] = uiResourceState{
Op: op,
Start: time.Now().Round(time.Second),
}
h.l.Unlock()

var operation string
Expand Down Expand Up @@ -113,22 +126,60 @@ func (h *UiHook) PreApply(
operation,
attrString)))

// Set a timer to show an operation is still happening
time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id) })

return terraform.HookActionContinue, nil
}

func (h *UiHook) stillApplying(id string) {
// Grab the operation. We defer the lock here to avoid the "still..."
// message showing up after a completion message.
h.l.Lock()
defer h.l.Unlock()
state, ok := h.resources[id]

// If the resource is out of the map it means we're done with it
if !ok {
return
}

var msg string
switch state.Op {
case uiResourceModify:
msg = "Still modifying..."
case uiResourceDestroy:
msg = "Still destroying..."
case uiResourceCreate:
msg = "Still creating..."
case uiResourceUnknown:
return
}

h.ui.Output(h.Colorize.Color(fmt.Sprintf(
"[reset][bold]%s: %s (%s elapsed)[reset_bold]",
id,
msg,
time.Now().Round(time.Second).Sub(state.Start),
)))

// Reschedule
time.AfterFunc(periodicUiTimer, func() { h.stillApplying(id) })
}

func (h *UiHook) PostApply(
n *terraform.InstanceInfo,
s *terraform.InstanceState,
applyerr error) (terraform.HookAction, error) {
id := n.HumanId()

h.l.Lock()
op := h.resources[id]
state := h.resources[id]
delete(h.resources, id)
h.l.Unlock()

var msg string
switch op {
switch state.Op {
case uiResourceModify:
msg = "Modifications complete"
case uiResourceDestroy:
Expand Down Expand Up @@ -205,7 +256,7 @@ func (h *UiHook) init() {
panic("colorize not given")
}

h.resources = make(map[string]uiResourceOp)
h.resources = make(map[string]uiResourceState)

// Wrap the ui so that it is safe for concurrency regardless of the
// underlying reader/writer that is in place.
Expand Down