Skip to content

Commit 2549af2

Browse files
committed
Add state failed
1 parent 99354fd commit 2549af2

File tree

12 files changed

+96
-65
lines changed

12 files changed

+96
-65
lines changed

cmd/arduino-app-cli/app/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (r appListResult) String() string {
8383
cmdutil.IDToAlias(app.ID),
8484
app.Name,
8585
app.Icon,
86-
app.Status,
86+
app.State,
8787
app.Example,
8888
})
8989
}

cmd/arduino-app-cli/app/start.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ func newStartCmd(cfg config.Configuration) *cobra.Command {
4747
return startHandler(cmd.Context(), cfg, app)
4848
},
4949
ValidArgsFunction: completion.ApplicationNamesWithFilterFunc(cfg, func(apps orchestrator.AppInfo) bool {
50-
return apps.Status != orchestrator.StatusStarting &&
51-
apps.Status != orchestrator.StatusRunning
50+
return apps.State != orchestrator.StatusStarting &&
51+
apps.State != orchestrator.StatusRunning
5252
}),
5353
}
5454
}

cmd/arduino-app-cli/app/stop.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ func newStopCmd(cfg config.Configuration) *cobra.Command {
4545
return stopHandler(cmd.Context(), app)
4646
},
4747
ValidArgsFunction: completion.ApplicationNamesWithFilterFunc(cfg, func(apps orchestrator.AppInfo) bool {
48-
return apps.Status == orchestrator.StatusStarting ||
49-
apps.Status == orchestrator.StatusRunning
48+
return apps.State == orchestrator.StatusStarting ||
49+
apps.State == orchestrator.StatusRunning
5050
}),
5151
}
5252
}

cmd/gendoc/docs.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ func NewOpenApiGenerator(version string) *Generator {
6969
openapi3.SchemaOrRef{
7070
Schema: &openapi3.Schema{
7171
UniqueItems: f.Ptr(true),
72-
Enum: f.Map(orchestrator.Status("").AllowedStatuses(), func(v orchestrator.Status) interface{} { return v }),
72+
Enum: f.Map(orchestrator.State("").AllowedStatuses(), func(v orchestrator.State) interface{} { return v }),
7373
Type: f.Ptr(openapi3.SchemaTypeString),
7474
Description: f.Ptr("Application status"),
75-
ReflectType: reflect.TypeOf(orchestrator.Status("")),
75+
ReflectType: reflect.TypeOf(orchestrator.State("")),
7676
},
7777
},
7878
)
@@ -205,7 +205,7 @@ func NewOpenApiGenerator(version string) *Generator {
205205
reflector.DefaultOptions = append(reflector.DefaultOptions,
206206
jsonschema.InterceptSchema(func(params jsonschema.InterceptSchemaParams) (stop bool, err error) {
207207

208-
if params.Value.Type() == reflect.TypeOf(orchestrator.Status("")) {
208+
if params.Value.Type() == reflect.TypeOf(orchestrator.State("")) {
209209
params.Schema.WithRef("#/components/schemas/Status")
210210
return true, nil
211211
}
@@ -632,8 +632,8 @@ Contains a JSON object with the details of an error.
632632
Path: "/v1/apps",
633633
Request: (*orchestrator.ListAppRequest)(nil),
634634
Parameters: (*struct {
635-
Filter string `query:"filter" description:"Filters apps by apps,examples,default"`
636-
Status orchestrator.Status `query:"status" description:"Filters applications by status"`
635+
Filter string `query:"filter" description:"Filters apps by apps,examples,default"`
636+
Status orchestrator.State `query:"status" description:"Filters applications by status"`
637637
})(nil),
638638
CustomSuccessResponse: &CustomResponseDef{
639639
ContentType: "application/json",

internal/api/handlers/app_list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,21 @@ func HandleAppList(
5151
showApps = slices.Contains(filters, "apps")
5252
}
5353

54-
var statusFilter orchestrator.Status
54+
var stateFilter orchestrator.State
5555
if status := queryParams.Get("status"); status != "" {
5656
status, err := orchestrator.ParseStatus(status)
5757
if err != nil {
5858
render.EncodeResponse(w, http.StatusBadRequest, models.ErrorResponse{Details: "invalid status filter"})
5959
return
6060
}
61-
statusFilter = status
61+
stateFilter = status
6262
}
6363

6464
res, err := orchestrator.ListApps(r.Context(), dockerCli, orchestrator.ListAppRequest{
6565
ShowApps: showApps,
6666
ShowExamples: showExamples,
6767
ShowOnlyDefault: showOnlyDefault,
68-
StatusFilter: statusFilter,
68+
StateFilter: stateFilter,
6969
}, idProvider, cfg)
7070
if err != nil {
7171
slog.Error("Unable to parse the app.yaml", slog.String("error", err.Error()))

internal/api/handlers/app_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func HandlerAppStatus(
4747
sseStream.SendError(render.SSEErrorData{Code: render.InternalServiceErr, Message: err.Error()})
4848
}
4949
for _, app := range result.Apps {
50-
if app.Status != "" {
50+
if app.State != "" {
5151
sseStream.Send(render.SSEEvent{Type: "app", Data: app})
5252
}
5353
}

internal/orchestrator/app_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func parseDockerStatusEvent(ctx context.Context, cfg config.Configuration, docke
115115
Name: app.Descriptor.Name,
116116
Description: app.Descriptor.Description,
117117
Icon: app.Descriptor.Icon,
118-
Status: appStatus.Status,
118+
State: appStatus.State,
119119
Example: id.IsExample(),
120120
Default: isDefault,
121121
}, nil

internal/orchestrator/helpers.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"context"
2020
"errors"
2121
"fmt"
22+
"log/slog"
2223
"slices"
2324
"strings"
2425

@@ -36,7 +37,12 @@ import (
3637

3738
type AppStatusInfo struct {
3839
AppPath *paths.Path
39-
Status Status
40+
State State
41+
}
42+
43+
type containerStateInfo struct {
44+
State State
45+
StatusMessage string
4046
}
4147

4248
// parseAppStatus takes all the containers that matches the DockerAppLabel,
@@ -52,19 +58,29 @@ type AppStatusInfo struct {
5258
// starting: at least one starting
5359
func parseAppStatus(containers []container.Summary) []AppStatusInfo {
5460
apps := make([]AppStatusInfo, 0, len(containers))
55-
appsStatusMap := make(map[string][]Status)
61+
appsStatusMap := make(map[string][]containerStateInfo)
5662
for _, c := range containers {
5763
appPath, ok := c.Labels[DockerAppPathLabel]
5864
if !ok {
5965
continue
6066
}
61-
appsStatusMap[appPath] = append(appsStatusMap[appPath], StatusFromDockerState(c.State))
67+
appsStatusMap[appPath] = append(appsStatusMap[appPath], containerStateInfo{
68+
State: StatusFromDockerState(c.State),
69+
StatusMessage: c.Status,
70+
})
71+
72+
slog.Debug("Container status",
73+
slog.String("appPath", appPath),
74+
slog.String("containerID", c.ID),
75+
slog.String("state", string(c.State)),
76+
slog.String("statusMessage", c.Status),
77+
)
6278
}
6379

64-
appendResult := func(appPath *paths.Path, status Status) {
80+
appendResult := func(appPath *paths.Path, status State) {
6581
apps = append(apps, AppStatusInfo{
6682
AppPath: appPath,
67-
Status: status,
83+
State: status,
6884
})
6985
}
7086

@@ -74,31 +90,37 @@ func parseAppStatus(containers []container.Summary) []AppStatusInfo {
7490
appPath := paths.New(appPath)
7591

7692
// running: all running
77-
if !slices.ContainsFunc(s, func(v Status) bool { return v != StatusRunning }) {
93+
if !slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State != StatusRunning }) {
7894
appendResult(appPath, StatusRunning)
7995
continue
8096
}
8197
// stopped: all stopped
82-
if !slices.ContainsFunc(s, func(v Status) bool { return v != StatusStopped }) {
98+
if !slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State != StatusStopped }) {
8399
appendResult(appPath, StatusStopped)
84100
continue
85101
}
86102

87103
// ...else we have multiple different status we calculate the status
88104
// among the possible left: {failed, stopping, starting}
89-
if slices.ContainsFunc(s, func(v Status) bool { return v == StatusFailed }) {
105+
if slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State == StatusFailed }) {
90106
appendResult(appPath, StatusFailed)
91107
continue
92108
}
93-
if slices.ContainsFunc(s, func(v Status) bool { return v == StatusStopping }) {
94-
appendResult(appPath, StatusStopping)
109+
if slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State == StatusStopped }) {
110+
appendResult(appPath, StatusFailed)
95111
continue
96112
}
97-
if slices.ContainsFunc(s, func(v Status) bool { return v == StatusStopped }) {
113+
if slices.ContainsFunc(s, func(v containerStateInfo) bool {
114+
return v.State == StatusStopped && strings.Contains(v.StatusMessage, "Exited (0)")
115+
}) {
98116
appendResult(appPath, StatusFailed)
99117
continue
100118
}
101-
if slices.ContainsFunc(s, func(v Status) bool { return v == StatusStarting }) {
119+
if slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State == StatusStopping }) {
120+
appendResult(appPath, StatusStopping)
121+
continue
122+
}
123+
if slices.ContainsFunc(s, func(v containerStateInfo) bool { return v.State == StatusStarting }) {
102124
appendResult(appPath, StatusStarting)
103125
continue
104126
}
@@ -193,7 +215,7 @@ func getRunningApp(
193215
return nil, fmt.Errorf("failed to get running apps: %w", err)
194216
}
195217
idx := slices.IndexFunc(apps, func(a AppStatusInfo) bool {
196-
return a.Status == StatusRunning || a.Status == StatusStarting
218+
return a.State == StatusRunning || a.State == StatusStarting
197219
})
198220
if idx == -1 {
199221
return nil, nil

internal/orchestrator/helpers_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestParseAppStatus(t *testing.T) {
2727
tests := []struct {
2828
name string
2929
containerState []container.ContainerState
30-
want Status
30+
want State
3131
}{
3232
{
3333
name: "everything running",
@@ -76,7 +76,7 @@ func TestParseAppStatus(t *testing.T) {
7676
})
7777
res := parseAppStatus(input)
7878
require.Len(t, res, 1)
79-
require.Equal(t, tc.want, res[0].Status)
79+
require.Equal(t, tc.want, res[0].State)
8080
require.Equal(t, "path1", res[0].AppPath.String())
8181
})
8282
}

internal/orchestrator/orchestrator.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,16 @@ func stopAppWithCmd(ctx context.Context, docker command.Cli, app app.ArduinoApp,
389389
ctx, cancel := context.WithCancel(ctx)
390390
defer cancel()
391391

392+
appStatus, err := getAppStatus(ctx, docker, app)
393+
if err != nil {
394+
yield(StreamMessage{error: err})
395+
return
396+
}
397+
if appStatus.State != StatusStarting && appStatus.State != StatusRunning {
398+
yield(StreamMessage{data: fmt.Sprintf("app %q is not running", app.Name)})
399+
return
400+
}
401+
392402
if !yield(StreamMessage{data: fmt.Sprintf("Stopping app %q", app.Name)}) {
393403
return
394404
}
@@ -410,7 +420,7 @@ func stopAppWithCmd(ctx context.Context, docker command.Cli, app app.ArduinoApp,
410420
yield(StreamMessage{error: err})
411421
return
412422
}
413-
if appStatus.Status != StatusStarting && appStatus.Status != StatusRunning {
423+
if appStatus.State != StatusStarting && appStatus.State != StatusRunning {
414424
yield(StreamMessage{data: fmt.Sprintf("app %q is not running", app.Name)})
415425
return
416426
}
@@ -513,7 +523,7 @@ func StartDefaultApp(
513523
if err != nil {
514524
return fmt.Errorf("failed to get app details: %w", err)
515525
}
516-
if status.Status == "running" {
526+
if status.State == "running" {
517527
return nil
518528
}
519529

@@ -537,7 +547,7 @@ type AppInfo struct {
537547
Name string `json:"name"`
538548
Description string `json:"description"`
539549
Icon string `json:"icon"`
540-
Status Status `json:"status,omitempty"`
550+
State State `json:"state,omitempty"`
541551
Example bool `json:"example"`
542552
Default bool `json:"default"`
543553
}
@@ -551,8 +561,7 @@ type ListAppRequest struct {
551561
ShowExamples bool
552562
ShowOnlyDefault bool
553563
ShowApps bool
554-
StatusFilter Status
555-
564+
StateFilter State
556565
// IncludeNonStandardLocationApps will include apps that are not in the standard apps directory.
557566
// We will search by looking for docker container metadata, and add the app not present in the
558567
// standard apps directory in the result list.
@@ -628,14 +637,14 @@ func ListApps(
628637
continue
629638
}
630639

631-
var status Status
640+
var state State
632641
if idx := slices.IndexFunc(apps, func(a AppStatusInfo) bool {
633642
return a.AppPath.EqualsTo(app.FullPath)
634643
}); idx != -1 {
635-
status = apps[idx].Status
644+
state = apps[idx].State
636645
}
637646

638-
if req.StatusFilter != "" && req.StatusFilter != status {
647+
if req.StateFilter != "" && req.StateFilter != state {
639648
continue
640649
}
641650

@@ -650,7 +659,7 @@ func ListApps(
650659
Name: app.Name,
651660
Description: app.Descriptor.Description,
652661
Icon: app.Descriptor.Icon,
653-
Status: status,
662+
State: state,
654663
Example: id.IsExample(),
655664
Default: isDefault,
656665
},
@@ -666,7 +675,7 @@ type AppDetailedInfo struct {
666675
Path string `json:"path"`
667676
Description string `json:"description"`
668677
Icon string `json:"icon"`
669-
Status Status `json:"status" required:"true"`
678+
State State `json:"state" required:"true"`
670679
Example bool `json:"example"`
671680
Default bool `json:"default"`
672681
Bricks []AppDetailedBrick `json:"bricks,omitempty"`
@@ -690,15 +699,15 @@ func AppDetails(
690699
var wg sync.WaitGroup
691700
wg.Add(2)
692701
var defaultAppPath string
693-
var status Status
702+
var state State
694703
go func() {
695704
defer wg.Done()
696705
app, err := getAppStatus(ctx, docker, userApp)
697706
if err != nil {
698707
slog.Warn("unable to get app status", slog.String("error", err.Error()), slog.String("path", userApp.FullPath.String()))
699-
status = StatusStopped
708+
state = StatusStopped
700709
} else {
701-
status = app.Status
710+
state = app.State
702711
}
703712
}()
704713
go func() {
@@ -727,7 +736,7 @@ func AppDetails(
727736
Path: userApp.FullPath.String(),
728737
Description: userApp.Descriptor.Description,
729738
Icon: userApp.Descriptor.Icon,
730-
Status: status,
739+
State: state,
731740
Example: id.IsExample(),
732741
Default: defaultAppPath == userApp.FullPath.String(),
733742
Bricks: f.Map(userApp.Descriptor.Bricks, func(b app.Brick) AppDetailedBrick {

0 commit comments

Comments
 (0)