Skip to content

Commit 11181d3

Browse files
authored
Allow node list to sort and filter by approval status (#3685)
When calling `node list`, it is possible to sort by various fields, but it is not possible to sort by approval status. Supports 'approval' (as an alias) and 'status' for `--order-by` when calling `node list`. This also work when specifying `--order-reversed`. ``` bacalhau node list --order-by status bacalhau node list --order-by status --order-reversed ``` In addition to sorting, it is also possible to only return nodes with a specific status. This is also applied when filtering by labels. ``` bacalhau node list --filter-status approved bacalhau node list --filter-status approved --labels env=devstack ``` Resolves #3682
1 parent 6f54e81 commit 11181d3

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

cmd/cli/node/list.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package node
22

33
import (
44
"fmt"
5+
"slices"
56

67
"github.com/spf13/cobra"
78
"golang.org/x/exp/maps"
@@ -14,14 +15,16 @@ import (
1415
)
1516

1617
var defaultColumnGroups = []string{"labels", "capacity"}
17-
var orderByFields = []string{"id", "type", "available_cpu", "available_memory", "available_disk", "available_gpu"}
18+
var orderByFields = []string{"id", "type", "available_cpu", "available_memory", "available_disk", "available_gpu", "status"}
19+
var filterStatusValues = []string{"approved", "pending", "rejected"}
1820

1921
// ListOptions is a struct to support node command
2022
type ListOptions struct {
2123
output.OutputOptions
2224
cliflags.ListOptions
23-
ColumnGroups []string
24-
Labels string
25+
ColumnGroups []string
26+
Labels string
27+
FilterByStatus string
2528
}
2629

2730
// NewListOptions returns initialized Options
@@ -43,11 +46,13 @@ func NewListCmd() *cobra.Command {
4346
}
4447
nodeCmd.Flags().StringSliceVar(&o.ColumnGroups, "show", o.ColumnGroups,
4548
fmt.Sprintf("What column groups to show. Zero or more of: %q", maps.Keys(toggleColumns)))
46-
4749
nodeCmd.Flags().StringVar(&o.Labels, "labels", o.Labels,
4850
"Filter nodes by labels. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more information.")
4951
nodeCmd.Flags().AddFlagSet(cliflags.ListFlags(&o.ListOptions))
5052
nodeCmd.Flags().AddFlagSet(cliflags.OutputFormatFlags(&o.OutputOptions))
53+
nodeCmd.Flags().StringVar(&o.FilterByStatus, "filter-status", o.FilterByStatus,
54+
fmt.Sprintf("Filter nodes by status. One of: %q", filterStatusValues))
55+
5156
return nodeCmd
5257
}
5358

@@ -63,8 +68,16 @@ func (o *ListOptions) run(cmd *cobra.Command, _ []string) {
6368
util.Fatal(cmd, fmt.Errorf("could not parse labels: %w", err), 1)
6469
}
6570
}
71+
72+
if o.FilterByStatus != "" {
73+
if !slices.Contains(filterStatusValues, o.FilterByStatus) {
74+
util.Fatal(cmd, fmt.Errorf("cannot use '%s' as filter status value, should be one of: %q", o.FilterByStatus, filterStatusValues), 1)
75+
}
76+
}
77+
6678
response, err := util.GetAPIClientV2(cmd).Nodes().List(ctx, &apimodels.ListNodesRequest{
67-
Labels: labelRequirements,
79+
Labels: labelRequirements,
80+
FilterByStatus: o.FilterByStatus,
6881
BaseListRequest: apimodels.BaseListRequest{
6982
Limit: o.Limit,
7083
NextToken: o.NextToken,

pkg/publicapi/apimodels/node.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ type GetNodeResponse struct {
1717

1818
type ListNodesRequest struct {
1919
BaseListRequest
20-
Labels []labels.Requirement `query:"-"` // don't auto bind as it requires special handling
20+
Labels []labels.Requirement `query:"-"` // don't auto bind as it requires special handling
21+
FilterByStatus string `query:"filter-status"`
2122
}
2223

2324
// ToHTTPRequest is used to convert the request to an HTTP request
@@ -27,6 +28,11 @@ func (o *ListNodesRequest) ToHTTPRequest() *HTTPRequest {
2728
for _, v := range o.Labels {
2829
r.Params.Add("labels", v.String())
2930
}
31+
32+
if o.FilterByStatus != "" {
33+
r.Params.Add("filter-status", o.FilterByStatus)
34+
}
35+
3036
return r
3137
}
3238

pkg/publicapi/endpoint/orchestrator/node.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package orchestrator
33
import (
44
"context"
55
"net/http"
6+
"strings"
67

78
"github.com/labstack/echo/v4"
89
"golang.org/x/exp/slices"
@@ -74,6 +75,10 @@ func (e *Endpoint) listNodes(c echo.Context) error {
7475
sortFnc = func(a, b *models.NodeInfo) int {
7576
return util.Compare[uint64]{}.CmpRev(capacity(a).GPU, capacity(b).GPU)
7677
}
78+
case "approval", "status":
79+
sortFnc = func(a, b *models.NodeInfo) int {
80+
return util.Compare[string]{}.Cmp(a.Approval.String(), b.Approval.String())
81+
}
7782
default:
7883
return echo.NewHTTPError(http.StatusBadRequest, "invalid order_by")
7984
}
@@ -97,9 +102,15 @@ func (e *Endpoint) listNodes(c echo.Context) error {
97102
return err
98103
}
99104

100-
// filter nodes
105+
args.FilterByStatus = strings.ToUpper(args.FilterByStatus)
106+
107+
// filter nodes, first by status, then by label selectors
101108
res := make([]*models.NodeInfo, 0)
102109
for i, node := range allNodes {
110+
if args.FilterByStatus != "" && args.FilterByStatus != node.Approval.String() {
111+
continue
112+
}
113+
103114
if selector.Matches(labels.Set(node.Labels)) {
104115
res = append(res, &allNodes[i])
105116
}

0 commit comments

Comments
 (0)