Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions cmd/arduino-app-cli/app/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@
package app

import (
"context"
"fmt"

"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/arduino/arduino-app-cli/cmd/arduino-app-cli/completion"
"github.com/arduino/arduino-app-cli/cmd/arduino-app-cli/internal/servicelocator"
"github.com/arduino/arduino-app-cli/cmd/feedback"
"github.com/arduino/arduino-app-cli/internal/orchestrator"
"github.com/arduino/arduino-app-cli/internal/orchestrator/app"
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
)

Expand All @@ -32,17 +40,63 @@ func newRestartCmd(cfg config.Configuration) *cobra.Command {
if len(args) == 0 {
return cmd.Help()
}
app, err := Load(args[0])
appToStart, err := Load(args[0])
if err != nil {
feedback.Fatal(err.Error(), feedback.ErrBadArgument)
return nil
}
if err := stopHandler(cmd.Context(), app); err != nil {
feedback.Warnf("failed to stop app: %s", err.Error())
}
return startHandler(cmd.Context(), cfg, app)
return restartHandler(cmd.Context(), cfg, appToStart)
},
ValidArgsFunction: completion.ApplicationNames(cfg),
}
return cmd
}

func restartHandler(ctx context.Context, cfg config.Configuration, app app.ArduinoApp) error {
out, _, getResult := feedback.OutputStreams()

stream := orchestrator.RestartApp(
ctx,
servicelocator.GetDockerClient(),
servicelocator.GetProvisioner(),
servicelocator.GetModelsIndex(),
servicelocator.GetBricksIndex(),
app,
cfg,
servicelocator.GetStaticStore(),
)
for message := range stream {
switch message.GetType() {
case orchestrator.ProgressType:
fmt.Fprintf(out, "Progress[%s]: %.0f%%\n", message.GetProgress().Name, message.GetProgress().Progress)
case orchestrator.InfoType:
fmt.Fprintln(out, "[INFO]", message.GetData())
case orchestrator.ErrorType:
errMesg := cases.Title(language.AmericanEnglish).String(message.GetError().Error())
feedback.Fatal(fmt.Sprintf("[ERROR] %s", errMesg), feedback.ErrGeneric)
return nil
}
}

outputResult := getResult()
feedback.PrintResult(restartAppResult{
AppName: app.Name,
Status: "restarted",
Output: outputResult,
})

return nil
}

type restartAppResult struct {
AppName string `json:"app_name"`
Status string `json:"status"`
Output *feedback.OutputStreamsResult `json:"output,omitempty"`
}

func (r restartAppResult) String() string {
return fmt.Sprintf("✓ App %q restarted successfully", r.AppName)
}

func (r restartAppResult) Data() interface{} {
return r
}
46 changes: 46 additions & 0 deletions internal/orchestrator/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ func StartApp(
yield(StreamMessage{error: fmt.Errorf("app %q is running", running.Name)})
return
}
if !yield(StreamMessage{data: fmt.Sprintf("Starting app %q", app.Name)}) {
return
}

if err := setStatusLeds(LedTriggerNone); err != nil {
slog.Debug("unable to set status leds", slog.String("error", err.Error()))
Expand Down Expand Up @@ -379,6 +382,9 @@ func stopAppWithCmd(ctx context.Context, app app.ArduinoApp, cmd string) iter.Se
ctx, cancel := context.WithCancel(ctx)
defer cancel()

if !yield(StreamMessage{data: fmt.Sprintf("Stopping app %q", app.Name)}) {
return
}
if err := setStatusLeds(LedTriggerDefault); err != nil {
slog.Debug("unable to set status leds", slog.String("error", err.Error()))
}
Expand Down Expand Up @@ -427,6 +433,46 @@ func StopAndDestroyApp(ctx context.Context, app app.ArduinoApp) iter.Seq[StreamM
return stopAppWithCmd(ctx, app, "down")
}

func RestartApp(
ctx context.Context,
docker command.Cli,
provisioner *Provision,
modelsIndex *modelsindex.ModelsIndex,
bricksIndex *bricksindex.BricksIndex,
appToStart app.ArduinoApp,
cfg config.Configuration,
staticStore *store.StaticStore,
) iter.Seq[StreamMessage] {
return func(yield func(StreamMessage) bool) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
runningApp, err := getRunningApp(ctx, docker.Client())
if err != nil {
yield(StreamMessage{error: err})
return
}

if runningApp != nil {
if runningApp.FullPath.String() != appToStart.FullPath.String() {
yield(StreamMessage{error: fmt.Errorf("another app %q is running", runningApp.Name)})
return
}

stopStream := StopApp(ctx, *runningApp)
for msg := range stopStream {
if !yield(msg) {
return
}
if msg.error != nil {
return
}
}
}
startStream := StartApp(ctx, docker, provisioner, modelsIndex, bricksIndex, appToStart, cfg, staticStore)
startStream(yield)
}
}

func StartDefaultApp(
ctx context.Context,
docker command.Cli,
Expand Down