Skip to content

Commit 9537ab4

Browse files
refactor cli (#479)
1 parent 6e8fd3e commit 9537ab4

File tree

29 files changed

+727
-592
lines changed

29 files changed

+727
-592
lines changed

cmd/arduino-app-cli/app.go

Lines changed: 0 additions & 419 deletions
This file was deleted.

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/bcmi-labs/orchestrator/internal/orchestrator"
9+
"github.com/bcmi-labs/orchestrator/internal/orchestrator/app"
10+
)
11+
12+
func NewAppCmd() *cobra.Command {
13+
appCmd := &cobra.Command{
14+
Use: "app",
15+
Short: "Manage Arduino Apps",
16+
Long: "A CLI tool to manage Arduino Apps, including starting, stopping, logging, and provisioning.",
17+
}
18+
19+
appCmd.AddCommand(newCreateCmd())
20+
appCmd.AddCommand(newStartCmd())
21+
appCmd.AddCommand(newStopCmd())
22+
appCmd.AddCommand(newRestartCmd())
23+
appCmd.AddCommand(newLogsCmd())
24+
appCmd.AddCommand(newListCmd())
25+
appCmd.AddCommand(newPsCmd())
26+
appCmd.AddCommand(newMonitorCmd())
27+
28+
return appCmd
29+
}
30+
31+
func Load(idOrPath string) (app.ArduinoApp, error) {
32+
id, err := orchestrator.ParseID(idOrPath)
33+
if err != nil {
34+
return app.ArduinoApp{}, fmt.Errorf("invalid app path: %s", idOrPath)
35+
}
36+
37+
return app.Load(id.ToPath().String())
38+
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package app
2+
3+
import (
4+
"context"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/internal/servicelocator"
9+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/results"
10+
"github.com/bcmi-labs/orchestrator/cmd/feedback"
11+
"github.com/bcmi-labs/orchestrator/internal/orchestrator"
12+
)
13+
14+
func newListCmd() *cobra.Command {
15+
var jsonFormat bool
16+
17+
cmd := &cobra.Command{
18+
Use: "list",
19+
Short: "List all running Python apps",
20+
Run: func(cmd *cobra.Command, args []string) {
21+
listHandler(cmd.Context())
22+
},
23+
}
24+
25+
cmd.Flags().BoolVarP(&jsonFormat, "json", "", false, "Output the list in json format")
26+
return cmd
27+
}
28+
29+
func listHandler(ctx context.Context) {
30+
res, err := orchestrator.ListApps(ctx,
31+
servicelocator.GetDockerClient(),
32+
orchestrator.ListAppRequest{
33+
ShowExamples: true,
34+
ShowApps: true,
35+
IncludeNonStandardLocationApps: true,
36+
},
37+
)
38+
if err != nil {
39+
feedback.Fatal(err.Error(), feedback.ErrGeneric)
40+
}
41+
42+
feedback.PrintResult(results.AppListResult{
43+
Apps: res.Apps,
44+
BrokenApps: res.BrokenApps,
45+
})
46+
}

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/completion"
10+
"github.com/bcmi-labs/orchestrator/cmd/feedback"
11+
"github.com/bcmi-labs/orchestrator/internal/orchestrator"
12+
"github.com/bcmi-labs/orchestrator/internal/orchestrator/app"
13+
)
14+
15+
func newLogsCmd() *cobra.Command {
16+
var tail uint64
17+
cmd := &cobra.Command{
18+
Use: "logs app_path",
19+
Short: "Show the logs of the Python app",
20+
Args: cobra.MaximumNArgs(1),
21+
RunE: func(cmd *cobra.Command, args []string) error {
22+
if len(args) == 0 {
23+
return cmd.Help()
24+
}
25+
app, err := Load(args[0])
26+
if err != nil {
27+
return err
28+
}
29+
return logsHandler(cmd.Context(), app, &tail)
30+
},
31+
ValidArgsFunction: completion.ApplicationNames(),
32+
}
33+
cmd.Flags().Uint64Var(&tail, "tail", 100, "Tail the last N logs")
34+
return cmd
35+
}
36+
37+
func logsHandler(ctx context.Context, app app.ArduinoApp, tail *uint64) error {
38+
stdout, _, err := feedback.DirectStreams()
39+
if err != nil {
40+
feedback.Fatal(err.Error(), feedback.ErrBadArgument)
41+
return nil
42+
}
43+
44+
logsIter, err := orchestrator.AppLogs(
45+
ctx,
46+
app,
47+
orchestrator.AppLogsRequest{
48+
ShowAppLogs: true,
49+
Follow: true,
50+
Tail: tail,
51+
},
52+
)
53+
if err != nil {
54+
feedback.Fatal(err.Error(), feedback.ErrGeneric)
55+
return nil
56+
}
57+
for msg := range logsIter {
58+
fmt.Fprintf(stdout, "[%s] %s\n", msg.Name, msg.Content)
59+
}
60+
return nil
61+
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package app
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
6+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/completion"
7+
)
8+
9+
func newMonitorCmd() *cobra.Command {
10+
return &cobra.Command{
11+
Use: "monitor",
12+
Short: "Monitor the Python app",
13+
RunE: func(cmd *cobra.Command, args []string) error {
14+
panic("not implemented")
15+
},
16+
ValidArgsFunction: completion.ApplicationNames(),
17+
}
18+
}

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package app
2+
3+
import (
4+
"context"
5+
6+
"github.com/arduino/go-paths-helper"
7+
"github.com/spf13/cobra"
8+
9+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/results"
10+
"github.com/bcmi-labs/orchestrator/cmd/feedback"
11+
"github.com/bcmi-labs/orchestrator/internal/orchestrator"
12+
)
13+
14+
func newCreateCmd() *cobra.Command {
15+
var (
16+
icon string
17+
bricks []string
18+
noPyton bool
19+
noSketch bool
20+
fromApp string
21+
)
22+
23+
cmd := &cobra.Command{
24+
Use: "new name",
25+
Short: "Creates a new app",
26+
Args: cobra.ExactArgs(1),
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
cobra.MinimumNArgs(1)
29+
name := args[0]
30+
return createHandler(cmd.Context(), name, icon, bricks, noPyton, noSketch, fromApp)
31+
},
32+
}
33+
34+
cmd.Flags().StringVarP(&icon, "icon", "i", "", "Icon for the app")
35+
cmd.Flags().StringVarP(&fromApp, "from-app", "", "", "Create the new app from the path of an existing app")
36+
cmd.Flags().StringArrayVarP(&bricks, "bricks", "b", []string{}, "List of bricks to include in the app")
37+
cmd.Flags().BoolVarP(&noPyton, "no-python", "", false, "Do not include Python files")
38+
cmd.Flags().BoolVarP(&noSketch, "no-sketch", "", false, "Do not include Sketch files")
39+
cmd.MarkFlagsMutuallyExclusive("no-python", "no-sketch")
40+
41+
return cmd
42+
}
43+
44+
func createHandler(ctx context.Context, name string, icon string, bricks []string, noPython, noSketch bool, fromApp string) error {
45+
if fromApp != "" {
46+
wd, err := paths.Getwd()
47+
if err != nil {
48+
feedback.Fatal(err.Error(), feedback.ErrGeneric)
49+
return nil
50+
}
51+
fromPath := paths.New(fromApp)
52+
if !fromPath.IsAbs() {
53+
fromPath = wd.JoinPath(fromPath)
54+
}
55+
id, err := orchestrator.NewIDFromPath(fromPath)
56+
if err != nil {
57+
feedback.Fatal(err.Error(), feedback.ErrBadArgument)
58+
return nil
59+
}
60+
61+
resp, err := orchestrator.CloneApp(ctx, orchestrator.CloneAppRequest{
62+
Name: &name,
63+
FromID: id,
64+
})
65+
if err != nil {
66+
feedback.Fatal(err.Error(), feedback.ErrGeneric)
67+
return nil
68+
}
69+
dst := resp.ID.ToPath()
70+
71+
feedback.PrintResult(results.CreateAppResult{
72+
Result: "ok",
73+
Message: "App created successfully",
74+
Path: dst.String(),
75+
})
76+
77+
} else {
78+
resp, err := orchestrator.CreateApp(ctx, orchestrator.CreateAppRequest{
79+
Name: name,
80+
Icon: icon,
81+
Bricks: bricks,
82+
SkipPython: noPython,
83+
SkipSketch: noSketch,
84+
})
85+
if err != nil {
86+
feedback.Fatal(err.Error(), feedback.ErrGeneric)
87+
return nil
88+
}
89+
feedback.PrintResult(results.CreateAppResult{
90+
Result: "ok",
91+
Message: "App created successfully",
92+
Path: resp.ID.ToPath().String(),
93+
})
94+
}
95+
return nil
96+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package app
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
func newPsCmd() *cobra.Command {
8+
return &cobra.Command{
9+
Use: "ps",
10+
Short: "Shows the list of running Arduino Apps",
11+
RunE: func(cmd *cobra.Command, args []string) error {
12+
panic("not implemented")
13+
},
14+
}
15+
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/completion"
9+
"github.com/bcmi-labs/orchestrator/cmd/feedback"
10+
)
11+
12+
func newRestartCmd() *cobra.Command {
13+
cmd := &cobra.Command{
14+
Use: "restart app_path",
15+
Short: "Restart or Start an Arduino app",
16+
Args: cobra.MaximumNArgs(1),
17+
RunE: func(cmd *cobra.Command, args []string) error {
18+
if len(args) == 0 {
19+
return cmd.Help()
20+
}
21+
app, err := Load(args[0])
22+
if err != nil {
23+
feedback.Fatal(err.Error(), feedback.ErrBadArgument)
24+
return nil
25+
}
26+
if err := stopHandler(cmd.Context(), app); err != nil {
27+
feedback.Warning(fmt.Sprintf("failed to stop app: %s", err.Error()))
28+
}
29+
return startHandler(cmd.Context(), app)
30+
},
31+
ValidArgsFunction: completion.ApplicationNames(),
32+
}
33+
return cmd
34+
}

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/completion"
10+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/internal/servicelocator"
11+
"github.com/bcmi-labs/orchestrator/cmd/arduino-app-cli/results"
12+
"github.com/bcmi-labs/orchestrator/cmd/feedback"
13+
"github.com/bcmi-labs/orchestrator/internal/orchestrator"
14+
"github.com/bcmi-labs/orchestrator/internal/orchestrator/app"
15+
)
16+
17+
func newStartCmd() *cobra.Command {
18+
return &cobra.Command{
19+
Use: "start app_path",
20+
Short: "Start an Arduino app",
21+
Args: cobra.MaximumNArgs(1),
22+
RunE: func(cmd *cobra.Command, args []string) error {
23+
if len(args) == 0 {
24+
return cmd.Help()
25+
}
26+
app, err := Load(args[0])
27+
if err != nil {
28+
return err
29+
}
30+
return startHandler(cmd.Context(), app)
31+
},
32+
ValidArgsFunction: completion.ApplicationNames(),
33+
}
34+
}
35+
36+
func startHandler(ctx context.Context, app app.ArduinoApp) error {
37+
out, _, getResult := feedback.OutputStreams()
38+
39+
stream := orchestrator.StartApp(
40+
ctx,
41+
servicelocator.GetDockerClient(),
42+
servicelocator.GetProvisioner(),
43+
servicelocator.GetModelsIndex(),
44+
servicelocator.GetBricksIndex(),
45+
app,
46+
)
47+
for message := range stream {
48+
switch message.GetType() {
49+
case orchestrator.ProgressType:
50+
fmt.Fprintf(out, "Progress: %.0f%%\n", message.GetProgress().Progress)
51+
case orchestrator.InfoType:
52+
fmt.Fprintln(out, "[INFO]", message.GetData())
53+
case orchestrator.ErrorType:
54+
feedback.Fatal(message.GetError().Error(), feedback.ErrGeneric)
55+
return nil
56+
}
57+
}
58+
outputResult := getResult()
59+
feedback.PrintResult(results.StartAppResult{
60+
AppName: app.Name,
61+
Status: "started",
62+
Output: outputResult,
63+
})
64+
65+
return nil
66+
}

0 commit comments

Comments
 (0)