mig-cmd: handle SIGINT nicer when interrupting an action

Use WaitGroups to avoid interleaving text if an action is cancelled.
This did not occur all the time, but this change should provide for more
consistent behavior.

mig-console didn't handle this at all; add a comment around this as well
and adjust mig-console to provide the additional extra parameter
required.
This commit is contained in:
Aaron Meihm 2017-09-19 17:20:59 -05:00
Родитель c6ec604c0f
Коммит 33a0a4ee8f
3 изменённых файлов: 52 добавлений и 14 удалений

Просмотреть файл

@ -1351,8 +1351,14 @@ func (cli Client) EvaluateAgentTarget(target string) (agents []mig.Agent, err er
}
// FollowAction continuously loops over an action and prints its completion status in os.Stderr.
// when the action reaches its expiration date, FollowAction prints its final status and returns.
func (cli Client) FollowAction(a mig.Action, total int) (err error) {
// When the action reaches its expiration date, FollowAction prints its final status and returns.
//
// a represents the action being followed, and total indicates the total number of agents
// the action was submitted to and is used to initialize the progress meter.
//
// stop is of type chan bool, and passing a value to this channel will cause the routine to return
// immediately.
func (cli Client) FollowAction(a mig.Action, total int, stop chan bool) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("followAction() -> %v", e)
@ -1362,12 +1368,19 @@ func (cli Client) FollowAction(a mig.Action, total int) (err error) {
previousctr := 0
status := ""
attempts := 0
cancelfollow := false
var completion float64
bar := pb.New(total)
bar.ShowSpeed = true
bar.SetMaxWidth(80)
bar.Output = os.Stderr
bar.Start()
go func() {
_ = <-stop
bar.Postfix(" [cancelling]")
cancelfollow = true
bar.Finish()
}()
for {
a, _, err = cli.GetAction(a.ID)
if err != nil {
@ -1390,6 +1403,10 @@ func (cli Client) FollowAction(a mig.Action, total int) (err error) {
goto finish
break
}
if cancelfollow {
// We have been asked to stop, just return
return nil
}
if a.Counters.Done > 0 && a.Counters.Done > previousctr {
completion = (float64(a.Counters.Done) / float64(a.Counters.Sent)) * 100
if completion < 99.5 {

Просмотреть файл

@ -276,7 +276,12 @@ times show the various timestamps of the action
fmt.Printf("Action '%s' successfully launched with ID '%.0f' on target '%s'\n",
a.Name, a.ID, a.Target)
if follow {
err = cli.FollowAction(a, tcount)
// XXX the sigint channel, which is used to indicate the follow
// operation should be cancelled is not actually used right now. This
// should likely be handled in a similar manner to how mig-cmd handles
// interrupts.
sigint := make(chan bool, 1)
err = cli.FollowAction(a, tcount, sigint)
if err != nil {
panic(err)
}

Просмотреть файл

@ -15,6 +15,7 @@ import (
"fmt"
"os"
"os/signal"
"sync"
"time"
"mig.ninja/mig"
@ -382,30 +383,45 @@ readytolaunch:
}
fmt.Fprintf(os.Stderr, "\x1b[33mGO\n\x1b[0m")
// launch and follow
// Launch the action
a, err = cli.PostAction(a)
if err != nil {
panic(err)
}
// Follow the action for completion, and handle an interrupt to abort waiting for
// completion, but still print out available results.
var wg sync.WaitGroup
c := make(chan os.Signal, 1)
done := make(chan bool, 1)
sigint := make(chan bool, 1)
complete := make(chan bool, 1)
signal.Notify(c, os.Interrupt)
cancelled := false
wg.Add(1)
go func() {
err = cli.FollowAction(a, len(agents))
defer wg.Done()
err = cli.FollowAction(a, len(agents), sigint)
if err != nil {
panic(err)
}
done <- true
complete <- true
}()
select {
case <-c:
fmt.Fprintf(os.Stderr, "\n[notice] stopped following action, but agents may still be running.\n")
wg.Add(1)
go func() {
defer wg.Done()
select {
case _ = <-c:
cancelled = true
sigint <- true
case _ = <-complete:
}
}()
wg.Wait()
if cancelled {
fmt.Fprintf(os.Stderr, "[notice] stopped following action, but agents may still be running.\n")
fmt.Fprintf(os.Stderr, "fetching available results:\n")
goto printresults
case <-done:
goto printresults
}
printresults:
err = cli.PrintActionResults(a, show, render)
if err != nil {
panic(err)