From fe571c6c3e88e828e68172c1eb22b338e614635b Mon Sep 17 00:00:00 2001 From: cskh Date: Fri, 21 Jul 2023 11:34:36 -0400 Subject: [PATCH 1/5] member cli: add -filter expression to flags --- agent/agent_endpoint.go | 15 +++++++++++++++ api/agent.go | 6 ++++++ api/agent_test.go | 10 ++++++++++ command/members/members.go | 3 +++ 4 files changed, 34 insertions(+) diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index 8057487b2b45..9ee6e41e1c85 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 f45929cb5b7a..b09ed1c1cd75 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 ebf738154781..dce525ebc322 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -155,6 +155,16 @@ 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)) } func TestAPI_AgentMembers(t *testing.T) { diff --git a/command/members/members.go b/command/members/members.go index e6be185e5328..9895837f6484 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 { From 779901b6cd428e1b5116a9fe33ff5f88ce27e88b Mon Sep 17 00:00:00 2001 From: cskh Date: Fri, 21 Jul 2023 11:49:58 -0400 Subject: [PATCH 2/5] changelog --- .changelog/18223.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18223.txt diff --git a/.changelog/18223.txt b/.changelog/18223.txt new file mode 100644 index 000000000000..067ca64f48e8 --- /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. +``` From adfa76146adabd4a6ed54f067e6e3e886d5c198f Mon Sep 17 00:00:00 2001 From: cskh Date: Fri, 21 Jul 2023 14:05:17 -0400 Subject: [PATCH 3/5] update doc --- website/content/commands/members.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/content/commands/members.mdx b/website/content/commands/members.mdx index 4e6b73cae288..ff1df561a6f4 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' From 28ab71e7e0b15769cc85ab3f8a10b99103ee1f44 Mon Sep 17 00:00:00 2001 From: cskh Date: Mon, 24 Jul 2023 18:29:45 -0400 Subject: [PATCH 4/5] Add test cases --- api/agent_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/agent_test.go b/api/agent_test.go index dce525ebc322..37f45f9997d3 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -165,6 +165,21 @@ func TestAPI_AgentMembersOpts(t *testing.T) { } require.Equal(t, 1, len(members)) + + members, err = agent.MembersOpts(MembersOpts{ + WAN: true, + Filter: `Tags["dc"] == notExist`, + }) + 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) { From 9f3d530d9d274095a09a26941a8406f945d00292 Mon Sep 17 00:00:00 2001 From: cskh Date: Mon, 24 Jul 2023 18:35:50 -0400 Subject: [PATCH 5/5] use quote --- api/agent_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/agent_test.go b/api/agent_test.go index 37f45f9997d3..e6731bb29ad6 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -168,7 +168,7 @@ func TestAPI_AgentMembersOpts(t *testing.T) { members, err = agent.MembersOpts(MembersOpts{ WAN: true, - Filter: `Tags["dc"] == notExist`, + Filter: `Tags["dc"] == "not-Exist"`, }) if err != nil { t.Fatalf("err: %v", err)