cli: Consolidate "still applying" messages

alisdair/still-applying-consolidation
Alisdair McDiarmid 5 years ago
parent e9c7f37b8c
commit cb5a90e59d

@ -33,8 +33,9 @@ func NewUiHook(view *View) *UiHook {
type UiHook struct {
terraform.NilHook
view *View
viewLock sync.Mutex
view *View
viewLock sync.Mutex
viewTimer *time.Timer
periodicUiTimer time.Duration
@ -50,10 +51,6 @@ type uiResourceState struct {
IDKey, IDValue string
Op uiResourceOp
Start time.Time
DoneCh chan struct{} // To be used for cancellation
done chan struct{} // used to coordinate tests
}
// uiResourceOp is an enum for operations on a resource
@ -120,69 +117,20 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation,
IDValue: idValue,
Op: op,
Start: time.Now().Round(time.Second),
DoneCh: make(chan struct{}),
done: make(chan struct{}),
}
h.resourcesLock.Lock()
h.resources[key] = uiState
h.resourcesLock.Unlock()
// Start goroutine that shows progress
go h.stillApplying(uiState)
return terraform.HookActionContinue, nil
}
func (h *UiHook) stillApplying(state uiResourceState) {
defer close(state.done)
for {
select {
case <-state.DoneCh:
return
case <-time.After(h.periodicUiTimer):
// Timer up, show status
}
var msg string
switch state.Op {
case uiResourceModify:
msg = "Still modifying..."
case uiResourceDestroy:
msg = "Still destroying..."
case uiResourceCreate:
msg = "Still creating..."
case uiResourceRead:
msg = "Still reading..."
case uiResourceUnknown:
return
}
idSuffix := ""
if state.IDKey != "" {
idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen))
}
h.println(fmt.Sprintf(
h.view.colorize.Color("[reset][bold]%s: %s [%s%s elapsed][reset]"),
state.DispAddr,
msg,
idSuffix,
time.Now().Round(time.Second).Sub(state.Start),
))
}
}
func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) {
id := addr.String()
h.resourcesLock.Lock()
state := h.resources[id]
if state.DoneCh != nil {
close(state.DoneCh)
}
delete(h.resources, id)
h.resourcesLock.Unlock()
@ -281,10 +229,46 @@ func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []prov
return terraform.HookActionContinue, nil
}
// Callback fired from a watchdog timer to ensure that we show some sign of
// life every few seconds, without flooding the UI with repeated messages
func (h *UiHook) heartbeat() {
h.resourcesLock.Lock()
defer h.resourcesLock.Unlock()
applying := len(h.resources)
var key string
for k := range h.resources {
key = k
break
}
if applying > 0 {
var suffix string
if applying > 1 {
resource := "resource"
if applying > 2 {
resource = "resources"
}
suffix = fmt.Sprintf(", along with %d other %s", applying-1, resource)
}
h.println(fmt.Sprintf(
h.view.colorize.Color("[reset][bold]%s: Still applying%s...[reset]"),
key,
suffix,
))
}
}
// Wrap calls to the view so that concurrent calls do not interleave println.
func (h *UiHook) println(s string) {
h.viewLock.Lock()
defer h.viewLock.Unlock()
if h.viewTimer == nil {
h.viewTimer = time.AfterFunc(h.periodicUiTimer, h.heartbeat)
} else {
h.viewTimer.Stop()
h.viewTimer.Reset(h.periodicUiTimer)
}
h.view.streams.Println(s)
}

@ -56,10 +56,8 @@ func TestUiHookPreApply_create(t *testing.T) {
t.Fatalf("Expected hook to continue, given: %#v", action)
}
// stop the background writer
uiState := h.resources[addr.String()]
close(uiState.DoneCh)
<-uiState.done
// stop the watchdog timer
h.viewTimer.Stop()
expectedOutput := "test_instance.foo: Creating...\n"
result := done(t)
@ -116,15 +114,13 @@ func TestUiHookPreApply_periodicTimer(t *testing.T) {
time.Sleep(3100 * time.Millisecond)
// stop the background writer
uiState := h.resources[addr.String()]
close(uiState.DoneCh)
<-uiState.done
// stop the watchdog timer
h.viewTimer.Stop()
expectedOutput := `test_instance.foo: Modifying... [id=test]
test_instance.foo: Still modifying... [id=test, 1s elapsed]
test_instance.foo: Still modifying... [id=test, 2s elapsed]
test_instance.foo: Still modifying... [id=test, 3s elapsed]
test_instance.foo: Still applying...
test_instance.foo: Still applying...
test_instance.foo: Still applying...
`
result := done(t)
output := result.Stdout()
@ -178,10 +174,8 @@ func TestUiHookPreApply_destroy(t *testing.T) {
t.Fatalf("Expected hook to continue, given: %#v", action)
}
// stop the background writer
uiState := h.resources[addr.String()]
close(uiState.DoneCh)
<-uiState.done
// stop the watchdog timer
h.viewTimer.Stop()
result := done(t)
expectedOutput := fmt.Sprintf("test_instance.foo (%s): Destroying... [id=abc123]\n", key)

Loading…
Cancel
Save