split UI from event handling

Signed-off-by: Alex Goodman <wagoodman@gmail.com>
This commit is contained in:
Alex Goodman
2021-06-26 23:32:28 -04:00
parent 706322f826
commit c5390264b0
17 changed files with 328 additions and 301 deletions
+77
View File
@@ -0,0 +1,77 @@
package cmd
import (
"errors"
"os"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"
)
// eventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and
// signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until
// an eventual graceful exit.
func eventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, ux ui.UI) error {
events := subscription.Events()
if err := setupUI(subscription.Unsubscribe, ux); err != nil {
return err
}
var retErr error
for {
if workerErrs == nil && events == nil {
break
}
select {
case err, isOpen := <-workerErrs:
if !isOpen {
workerErrs = nil
continue
}
if err != nil {
retErr = err
}
case e, isOpen := <-events:
if !isOpen {
events = nil
continue
}
if err := ux.Handle(e); err != nil {
if errors.Is(err, partybus.ErrUnsubscribe) {
log.Warnf("unable to unsubscribe from the event bus")
events = nil
} else {
retErr = multierror.Append(retErr, err)
// TODO: should we unsubscribe? should we try to halt execution? or continue?
}
}
case <-signals:
if err := subscription.Unsubscribe(); err != nil {
log.Warnf("unable to unsubscribe from the event bus: %+v", err)
events = nil
}
}
}
if err := ux.Teardown(); err != nil {
retErr = multierror.Append(retErr, err)
}
return retErr
}
func setupUI(unsubscribe func() error, ux ui.UI) error {
if err := ux.Setup(unsubscribe); err != nil {
ux = ui.NewLoggerUI()
if err := ux.Setup(unsubscribe); err != nil {
// something is very wrong, bail.
return err
}
log.Errorf("unable to setup given UI, falling back to logger: %+v", err)
}
return nil
}
+6 -3
View File
@@ -186,9 +186,12 @@ func bindPackagesConfigOptions(flags *pflag.FlagSet) error {
}
func packagesExec(_ *cobra.Command, args []string) error {
errs := packagesExecWorker(args[0])
ux := ui.Select(appConfig.CliOptions.Verbosity > 0, appConfig.Quiet)
return ux(errs, eventSubscription)
return eventLoop(
packagesExecWorker(args[0]),
setupSignals(),
eventSubscription,
ui.Select(appConfig.CliOptions.Verbosity > 0, appConfig.Quiet),
)
}
func packagesExecWorker(userInput string) <-chan error {
+6 -3
View File
@@ -63,9 +63,12 @@ func init() {
}
func powerUserExec(_ *cobra.Command, args []string) error {
errs := powerUserExecWorker(args[0])
ux := ui.Select(appConfig.CliOptions.Verbosity > 0, appConfig.Quiet)
return ux(errs, eventSubscription)
return eventLoop(
powerUserExecWorker(args[0]),
setupSignals(),
eventSubscription,
ui.Select(appConfig.CliOptions.Verbosity > 0, appConfig.Quiet),
)
}
func powerUserExecWorker(userInput string) <-chan error {
+20
View File
@@ -0,0 +1,20 @@
package cmd
import (
"os"
"os/signal"
"syscall"
)
func setupSignals() <-chan os.Signal {
c := make(chan os.Signal, 1) // Note: A buffered channel is recommended for this; see https://golang.org/pkg/os/signal/#Notify
interruptions := []os.Signal{
syscall.SIGINT,
syscall.SIGTERM,
}
signal.Notify(c, interruptions...)
return c
}