Skip to content

Commit

Permalink
fix: run the interactive installer loop to report errors
Browse files Browse the repository at this point in the history
In the previous implementation, even though `installer.err` was set, it
was never checked 🤦.

The run loop was stolen from the dashboard code.

Fixes #8205

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
  • Loading branch information
smira committed Jan 31, 2024
1 parent 87be76b commit 593afee
Showing 1 changed file with 55 additions and 64 deletions.
119 changes: 55 additions & 64 deletions internal/pkg/tui/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 593afee

Please sign in to comment.