From 31d2813714290f7bfb16d493a767228edd4ed9fa Mon Sep 17 00:00:00 2001 From: cskh Date: Tue, 25 Jul 2023 13:54:52 -0400 Subject: [PATCH] member cli: add -filter expression to flags (#18223) * member cli: add -filter expression to flags * changelog * update doc * Add test cases * use quote --- .changelog/18223.txt | 3 +++ agent/agent_endpoint.go | 15 +++++++++++++++ api/agent.go | 6 ++++++ api/agent_test.go | 25 +++++++++++++++++++++++++ command/members/members.go | 3 +++ website/content/commands/members.mdx | 6 ++++++ 6 files changed, 58 insertions(+) create mode 100644 .changelog/18223.txt diff --git a/.changelog/18223.txt b/.changelog/18223.txt new file mode 100644 index 0000000000..067ca64f48 --- /dev/null +++ b/.changelog/18223.txt @@ -0,0 +1,3 @@ +```release-note:feature +cli: `consul members` command uses `-filter` expression to filter members based on bexpr. +``` diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index 8057487b2b..9ee6e41e1c 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -619,6 +619,21 @@ func (s *HTTPHandlers) AgentMembers(resp http.ResponseWriter, req *http.Request) } } + // filter the members by parsed filter expression + var filterExpression string + s.parseFilter(req, &filterExpression) + if filterExpression != "" { + filter, err := bexpr.CreateFilter(filterExpression, nil, members) + if err != nil { + return nil, err + } + raw, err := filter.Execute(members) + if err != nil { + return nil, err + } + members = raw.([]serf.Member) + } + total := len(members) if err := s.agent.filterMembers(token, &members); err != nil { return nil, err diff --git a/api/agent.go b/api/agent.go index f45929cb5b..b09ed1c1cd 100644 --- a/api/agent.go +++ b/api/agent.go @@ -274,6 +274,8 @@ type MembersOpts struct { // Segment is the LAN segment to show members for. Setting this to the // AllSegments value above will show members in all segments. Segment string + + Filter string } // AgentServiceRegistration is used to register a new service @@ -790,6 +792,10 @@ func (a *Agent) MembersOpts(opts MembersOpts) ([]*AgentMember, error) { r.params.Set("wan", "1") } + if opts.Filter != "" { + r.params.Set("filter", opts.Filter) + } + _, resp, err := a.c.doRequest(r) if err != nil { return nil, err diff --git a/api/agent_test.go b/api/agent_test.go index ebf7381547..e6731bb29a 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -155,6 +155,31 @@ func TestAPI_AgentMembersOpts(t *testing.T) { if len(members) != 2 { t.Fatalf("bad: %v", members) } + + members, err = agent.MembersOpts(MembersOpts{ + WAN: true, + Filter: `Tags["dc"] == dc2`, + }) + if err != nil { + t.Fatalf("err: %v", err) + } + + require.Equal(t, 1, len(members)) + + members, err = agent.MembersOpts(MembersOpts{ + WAN: true, + Filter: `Tags["dc"] == "not-Exist"`, + }) + if err != nil { + t.Fatalf("err: %v", err) + } + require.Equal(t, 0, len(members)) + + _, err = agent.MembersOpts(MembersOpts{ + WAN: true, + Filter: `Tags["dc"] == invalid-bexpr-value`, + }) + require.ErrorContains(t, err, "Failed to create boolean expression evaluator") } func TestAPI_AgentMembers(t *testing.T) { diff --git a/command/members/members.go b/command/members/members.go index e6be185e53..9895837f64 100644 --- a/command/members/members.go +++ b/command/members/members.go @@ -33,6 +33,7 @@ type cmd struct { wan bool statusFilter string segment string + filter string } func New(ui cli.Ui) *cmd { @@ -54,6 +55,7 @@ func (c *cmd) init() { c.flags.StringVar(&c.segment, "segment", consulapi.AllSegments, "(Enterprise-only) If provided, output is filtered to only nodes in"+ "the given segment.") + c.flags.StringVar(&c.filter, "filter", "", "Filter to use with the request") c.http = &flags.HTTPFlags{} flags.Merge(c.flags, c.http.ClientFlags()) @@ -83,6 +85,7 @@ func (c *cmd) Run(args []string) int { opts := consulapi.MembersOpts{ Segment: c.segment, WAN: c.wan, + Filter: c.filter, } members, err := client.Agent().MembersOpts(opts) if err != nil { diff --git a/website/content/commands/members.mdx b/website/content/commands/members.mdx index 4e6b73cae2..ff1df561a6 100644 --- a/website/content/commands/members.mdx +++ b/website/content/commands/members.mdx @@ -48,6 +48,12 @@ Usage: `consul members [options]` in the WAN gossip pool. These are generally all the server nodes in each datacenter. +- `-filter=` - Expression to use for filtering the results, + e.g., `-filter='Tags["dc"] == dc2'`. + See the [`/catalog/nodes` API documentation](/consul/api-docs/catalog#filtering) for a + description of what is filterable. + + #### Enterprise Options @include 'http_api_partition_options.mdx'