Skip to content

Commit 367ef60

Browse files
authored
Merge pull request #140 from trickest/feat/stop-cmd-status-flag
Add status flag to the stop command
2 parents 41cba4a + e65fa1b commit 367ef60

File tree

7 files changed

+107
-23
lines changed

7 files changed

+107
-23
lines changed

README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ More example workflow **config.yaml** files can be found in the [Trickest Workfl
162162

163163
### Continuous Integration
164164

165-
You can find the Github Action for the `trickest-cli` at https://github.com/trickest/action and the Docker image at https://quay.io/trickest/trickest-cli.
165+
You can find the GitHub Action for the `trickest-cli` at https://github.com/trickest/action and the Docker image at https://quay.io/trickest/trickest-cli.
166166

167167
The `execute` command can be used as part of a CI pipeline to execute your Trickest workflows whenever your code or infrastructure changes. Optionally, you can use the `--watch` command inside the action to watch a workflow's progress until it completes.
168168

@@ -216,16 +216,17 @@ Use the **stop** command to stop a running workflow or node
216216
trickest stop --workflow <workflow_name> --space <space_name> [--run <run_id>] [--all] [--nodes <node_name_or_id>] [--child <child_task_index>]
217217
```
218218

219-
| Flag | Type | Default | Description |
220-
| ---------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
221-
| --url | string | / | URL copied from the Trickest platform, referencing a workflow and, optionally, a run/node |
222-
| --workflow | string | / | The name of the workflow. |
223-
| --project | string | / | The name of the project to which workflow belongs |
224-
| --space | string | / | The name of the space to which workflow belongs |
225-
| --run | string | / | Stop a specific run |
226-
| --all | bool | false | Stop all runs |
227-
| --nodes | string | / | A comma-separated list of nodes to stop. If none specified, the entire run will be stopped. If a node is a task group, the `--child` flag must be used |
228-
| --child | string | / | A comma-separated list or range of child tasks to stop. Example: `--child 1,2,3` or `--child 1-3` will stop the first three tasks in the specified node's taskgroup |
219+
| Flag | Type | Default | Description |
220+
|------------|--------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
221+
| --url | string | / | URL copied from the Trickest platform, referencing a workflow and, optionally, a run/node |
222+
| --workflow | string | / | The name of the workflow. |
223+
| --project | string | / | The name of the project to which workflow belongs |
224+
| --space | string | / | The name of the space to which workflow belongs |
225+
| --run | string | / | Stop a specific run |
226+
| --all | bool | false | Stop all runs |
227+
| --nodes | string | / | A comma-separated list of nodes to stop. If none specified, the entire run will be stopped. If a node is a task group, the `--child` flag must be used |
228+
| --child | string | / | A comma-separated list or range of child tasks to stop. Example: `--child 1,2,3` or `--child 1-3` will stop the first three tasks in the specified node's taskgroup |
229+
| --status | string | running | A comma-separated list of run statuses to evaluate for stopping. Example: `pending,submitted,running` |
229230

230231
## Output
231232
Use the **output** command to download the outputs of your particular workflow execution(s) to your local environment.
@@ -442,7 +443,7 @@ trickest scripts delete [--id <script_id>] [--name <script_name>]
442443
## Report Bugs / Feedback
443444
We look forward to any feedback you want to share with us or if you're stuck with a problem you can contact us at [[email protected]](mailto:[email protected]).
444445

445-
You can also create an [Issue](https://github.com/trickest/trickest-cli/issues/new/choose) in the Github repository.
446+
You can also create an [Issue](https://github.com/trickest/trickest-cli/issues/new/choose) in the GitHub repository.
446447

447448
[<img src="./banner.png" />](https://trickest.io/auth/register)
448449

cmd/get/json_output.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ type JSONSubJob struct {
6464
func NewJSONRun(run *trickest.Run, subjobs []trickest.SubJob, taskGroupStatsMap map[uuid.UUID]stats.TaskGroupStats) *JSONRun {
6565
jsonRun := &JSONRun{
6666
ID: *run.ID,
67-
Status: run.Status,
67+
Status: run.Status.String(),
6868
Author: run.Author,
6969
CreationType: run.CreationType,
7070
CreatedDate: run.CreatedDate,

cmd/stop/stop.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@ type Config struct {
2222
Nodes []string
2323
ChildrenRanges []string
2424
Children []int
25+
RunStatuses []string
2526
}
2627

2728
var cfg = &Config{}
29+
var defaultStatuses = trickest.RunStatuses{trickest.RunStatusRunning}.Strings()
2830

2931
func init() {
3032
StopCmd.Flags().BoolVar(&cfg.RunSpec.AllRuns, "all", false, "Stop all runs")
3133
StopCmd.Flags().StringVar(&cfg.RunSpec.RunID, "run", "", "Stop a specific run")
3234
StopCmd.Flags().StringSliceVar(&cfg.Nodes, "nodes", []string{}, "Nodes to stop. If none specified, the entire run will be stopped. If a node is a task group, the `--child` flag must be used (can be used multiple times)")
3335
StopCmd.Flags().StringSliceVar(&cfg.ChildrenRanges, "child", []string{}, "Child tasks to stop. If a node is a task group, the `--child` flag must be used (can be used multiple times)")
36+
StopCmd.Flags().StringSliceVar(&cfg.RunStatuses, "status", defaultStatuses, "Run statuses to be evaluated for stopping (can be used multiple times or comma-separated, e.g. `pending,submitted`)")
3437
}
3538

3639
// StopCmd represents the stop command
@@ -93,14 +96,29 @@ func run(cfg *Config) error {
9396
return fmt.Errorf("failed to create client: %w", err)
9497
}
9598

99+
for _, status := range cfg.RunStatuses {
100+
parsedStatus := trickest.RunStatus(strings.ToUpper(strings.TrimSpace(status)))
101+
if !parsedStatus.Valid() {
102+
return fmt.Errorf("invalid run status: %s", status)
103+
}
104+
if !parsedStatus.CanStop() {
105+
return fmt.Errorf("cannot stop runs with status: %s", status)
106+
}
107+
cfg.RunSpec.RunStatuses = append(cfg.RunSpec.RunStatuses, parsedStatus)
108+
}
109+
96110
ctx := context.Background()
97111

98-
cfg.RunSpec.RunStatus = "RUNNING"
99112
runs, err := cfg.RunSpec.GetRuns(ctx, client)
100113
if err != nil {
101114
return fmt.Errorf("failed to get runs: %w", err)
102115
}
103116

117+
if len(runs) == 0 {
118+
fmt.Println("No runs found")
119+
return nil
120+
}
121+
104122
var errs []error
105123
for _, run := range runs {
106124
if len(cfg.Nodes) == 0 && len(cfg.Children) == 0 {

pkg/actions/output.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func PrintDownloadResults(results []DownloadResult, runID uuid.UUID, destination
4646
// DownloadRunOutput downloads the outputs for the specified nodes in the run
4747
// Returns the download result summary, the directory where the outputs were saved, and an error if _all_ of the downloads failed
4848
func DownloadRunOutput(client *trickest.Client, run *trickest.Run, nodes []string, files []string, destinationPath string) ([]DownloadResult, string, error) {
49-
if run.Status == "PENDING" || run.Status == "SUBMITTED" {
49+
if !run.Status.IsStarted() {
5050
return nil, "", fmt.Errorf("run %s has not started yet (status: %s)", run.ID.String(), run.Status)
5151
}
5252

pkg/config/workflowrunspec.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type WorkflowRunSpec struct {
1515
RunID string
1616
NumberOfRuns int
1717
AllRuns bool
18-
RunStatus string
18+
RunStatuses trickest.RunStatuses
1919

2020
// Workflow identification
2121
SpaceName string
@@ -36,8 +36,8 @@ func (s WorkflowRunSpec) GetRuns(ctx context.Context, client *trickest.Client) (
3636
if err != nil {
3737
return nil, err
3838
}
39-
if s.RunStatus != "" && run.Status != s.RunStatus {
40-
return nil, fmt.Errorf("run %s has status %q, expected status %q", run.ID, run.Status, s.RunStatus)
39+
if len(s.RunStatuses) > 0 && !s.RunStatuses.Contains(run.Status) {
40+
return nil, fmt.Errorf("run %s has status %q, expected status %q", run.ID, run.Status, s.RunStatuses.String())
4141
}
4242
return []trickest.Run{*run}, nil
4343
}
@@ -53,7 +53,7 @@ func (s WorkflowRunSpec) GetRuns(ctx context.Context, client *trickest.Client) (
5353
limit = s.NumberOfRuns
5454
}
5555

56-
runs, err := client.GetRuns(ctx, workflow.ID, s.RunStatus, limit)
56+
runs, err := client.GetRuns(ctx, workflow.ID, s.RunStatuses.String(), limit)
5757
if err != nil {
5858
return nil, fmt.Errorf("failed to get runs: %w", err)
5959
}

pkg/display/run/printer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (p *RunPrinter) PrintAll(run *trickest.Run, subJobs []trickest.SubJob, vers
5757

5858
// Print basic run details
5959
output.WriteString(p.formatKeyValue("Name", run.WorkflowName))
60-
output.WriteString(p.formatKeyValue("Status", run.Status))
60+
output.WriteString(p.formatKeyValue("Status", run.Status.String()))
6161
output.WriteString(p.formatKeyValue("Machines", fmt.Sprintf("%d", run.Machines)))
6262
output.WriteString(p.formatKeyValue("Parallelism", fmt.Sprintf("%d", run.Parallelism)))
6363
output.WriteString(p.formatKeyValue("Fleet", run.FleetName))
@@ -68,7 +68,7 @@ func (p *RunPrinter) PrintAll(run *trickest.Run, subJobs []trickest.SubJob, vers
6868
output.WriteString(p.formatKeyValue("Created",
6969
run.CreatedDate.In(time.Local).Format(time.RFC1123)+" ("+FormatDuration(time.Since(*run.CreatedDate))+" ago)"))
7070
}
71-
if run.Status != "PENDING" && run.StartedDate != nil {
71+
if run.Status.IsStarted() && run.StartedDate != nil {
7272
output.WriteString(p.formatKeyValue("Started",
7373
run.StartedDate.In(time.Local).Format(time.RFC1123)+" ("+FormatDuration(time.Since(*run.StartedDate))+" ago)"))
7474
}
@@ -110,7 +110,7 @@ func (p *RunPrinter) PrintAll(run *trickest.Run, subJobs []trickest.SubJob, vers
110110
// - If the run is still running, mark them as "pending"
111111
// - If the run is finished, mark them as "stopped"
112112
defaultSubJobStatus := "stopped"
113-
if run.Status == "RUNNING" {
113+
if run.Status == trickest.RunStatusRunning {
114114
defaultSubJobStatus = "pending"
115115
}
116116
output.WriteString(p.formatSubJobTree(subJobs, version, defaultSubJobStatus, includeTaskGroupStats))

pkg/trickest/run.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,32 @@ import (
66
"fmt"
77
"net/http"
88
"net/url"
9+
"slices"
10+
"strings"
911
"time"
1012

1113
"github.com/google/uuid"
1214
)
1315

16+
const (
17+
RunStatusPending RunStatus = "PENDING"
18+
RunStatusSubmitted RunStatus = "SUBMITTED"
19+
RunStatusRunning RunStatus = "RUNNING"
20+
RunStatusCompleted RunStatus = "COMPLETED"
21+
RunStatusFailed RunStatus = "FAILED"
22+
RunStatusStopping RunStatus = "STOPPING"
23+
RunStatusStopped RunStatus = "STOPPED"
24+
)
25+
26+
type RunStatus string
27+
28+
type RunStatuses []RunStatus
29+
1430
// Run represents a workflow run
1531
type Run struct {
1632
ID *uuid.UUID `json:"id,omitempty"`
1733
Name string `json:"name,omitempty"`
18-
Status string `json:"status,omitempty"`
34+
Status RunStatus `json:"status,omitempty"`
1935
Machines int `json:"machines,omitempty"`
2036
Parallelism int `json:"parallelism,omitempty"`
2137
WorkflowVersionInfo *uuid.UUID `json:"workflow_version_info,omitempty"`
@@ -73,6 +89,55 @@ type RunSubJobInsights struct {
7389
Stopped int `json:"stopped"`
7490
}
7591

92+
func (s RunStatus) Valid() bool {
93+
switch s {
94+
case RunStatusPending, RunStatusSubmitted, RunStatusRunning,
95+
RunStatusCompleted, RunStatusFailed,
96+
RunStatusStopping, RunStatusStopped:
97+
return true
98+
default:
99+
return false
100+
}
101+
}
102+
103+
func (s RunStatus) IsStarted() bool {
104+
switch s {
105+
case RunStatusRunning, RunStatusCompleted, RunStatusFailed, RunStatusStopping, RunStatusStopped:
106+
return true
107+
default:
108+
return false
109+
}
110+
}
111+
112+
func (s RunStatus) CanStop() bool {
113+
switch s {
114+
case RunStatusPending, RunStatusSubmitted, RunStatusRunning:
115+
return true
116+
default:
117+
return false
118+
}
119+
}
120+
121+
func (s RunStatus) String() string {
122+
return string(s)
123+
}
124+
125+
func (s RunStatuses) Contains(status RunStatus) bool {
126+
return slices.Contains(s, status)
127+
}
128+
129+
func (s RunStatuses) Strings() []string {
130+
result := make([]string, len(s))
131+
for i, status := range s {
132+
result[i] = status.String()
133+
}
134+
return result
135+
}
136+
137+
func (s RunStatuses) String() string {
138+
return strings.Join(s.Strings(), ",")
139+
}
140+
76141
// GetRun retrieves a run by ID
77142
func (c *Client) GetRun(ctx context.Context, id uuid.UUID) (*Run, error) {
78143
var run Run

0 commit comments

Comments
 (0)