diff --git a/internal/pkg/tui/installer/installer.go b/internal/pkg/tui/installer/installer.go index 383b171f63..f62f2b9fdd 100644 --- a/internal/pkg/tui/installer/installer.go +++ b/internal/pkg/tui/installer/installer.go @@ -9,10 +9,10 @@ import ( "context" "fmt" "strings" - "sync" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" + "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/pkg/tui/components" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" @@ -38,8 +38,6 @@ type Page struct { type Installer struct { pages *tview.Pages app *tview.Application - wg sync.WaitGroup - err error ctx context.Context //nolint:containedctx cancel context.CancelFunc addedPages map[string]bool @@ -73,85 +71,78 @@ const ( // Run starts interactive installer. func (installer *Installer) Run(conn *Connection) error { - installer.startApp() - defer installer.stopApp() + installer.app = tview.NewApplication() - var ( - err error - description string - ) + var eg *errgroup.Group - for phase := phaseInit; phase <= phaseApply; { - switch phase { - case phaseInit: - description = "get the node information" - err = installer.init(conn) - case phaseConfigure: - description = "generate the configuration" - err = installer.configure() - case phaseApply: - description = "apply the configuration" - err = installer.apply(conn) - } + eg, installer.ctx = errgroup.WithContext(installer.ctx) - if err != nil && err != context.Canceled { - choice := installer.showModal( - fmt.Sprintf("Failed to %s", description), - err.Error(), - "Quit", "Retry", - ) + eg.Go(func() error { + defer installer.cancel() - if choice == 1 { - // apply should be retried from configure - if phase == phaseApply { - phase = phaseConfigure - } + return installer.app.SetRoot(installer.pages, true).EnableMouse(true).Run() + }) - continue - } - } + eg.Go(func() error { + defer installer.app.Stop() - if err != nil { - return err - } + <-installer.ctx.Done() - phase++ - } + return nil + }) - return nil -} + eg.Go(func() error { + defer installer.cancel() -func (installer *Installer) startApp() { - if installer.app != nil { - return - } + var ( + err error + description string + ) - installer.wg.Add(1) - installer.app = tview.NewApplication() + for phase := phaseInit; phase <= phaseApply; { + switch phase { + case phaseInit: + description = "get the node information" + err = installer.init(conn) + case phaseConfigure: + description = "generate the configuration" + err = installer.configure() + case phaseApply: + description = "apply the configuration" + err = installer.apply(conn) + } - go func() { - defer installer.wg.Done() - defer installer.cancel() + if err != nil && err != context.Canceled { + choice := installer.showModal( + fmt.Sprintf("Failed to %s", description), + err.Error(), + "Quit", "Retry", + ) + + if choice == 1 { + // apply should be retried from configure + if phase == phaseApply { + phase = phaseConfigure + } - if err := installer.app.SetRoot(installer.pages, true).EnableMouse(true).Run(); err != nil { - installer.err = err + continue + } + } + + if err != nil { + return err + } + + phase++ } - }() -} -func (installer *Installer) stopApp() { - if installer.app == nil { - return - } + return nil + }) - installer.app.Stop() - installer.wg.Wait() - installer.app = nil + return eg.Wait() } func (installer *Installer) init(conn *Connection) (err error) { - installer.startApp() - s := components.NewSpinner( fmt.Sprintf("Connecting to the maintenance service at [green::]%s[white::]", conn.nodeEndpoint), spinner,