diff --git a/.changelog/17754.txt b/.changelog/17754.txt index becf2c6c164e..56ab20dc213e 100644 --- a/.changelog/17754.txt +++ b/.changelog/17754.txt @@ -1,3 +1,3 @@ ```release-note:feature ui: consul version is displayed in nodes list with filtering and sorting based on versions -``` \ No newline at end of file +``` diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index 9f4210ac892a..0a37ae174a19 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -1508,7 +1508,8 @@ func TestAgent_Self(t *testing.T) { require.NoError(t, err) require.Equal(t, cs[a.config.SegmentName], val.Coord) - delete(val.Meta, structs.MetaSegmentKey) // Added later, not in config. + delete(val.Meta, structs.MetaSegmentKey) // Added later, not in config. + delete(val.Meta, structs.MetaConsulVersion) // Added later, not in config. require.Equal(t, a.config.NodeMeta, val.Meta) if tc.expectXDS { diff --git a/agent/consul/state/catalog.go b/agent/consul/state/catalog.go index 74efc3229556..4e9fcf716c47 100644 --- a/agent/consul/state/catalog.go +++ b/agent/consul/state/catalog.go @@ -3450,6 +3450,13 @@ func parseNodes(tx ReadTxn, ws memdb.WatchSet, idx uint64, ws.AddWithLimit(watchLimit, services.WatchCh(), allServicesCh) for service := services.Next(); service != nil; service = services.Next() { ns := service.(*structs.ServiceNode).ToNodeService() + // If version isn't defined in node meta, set it from the Consul service meta + if _, ok := dump.Meta[structs.MetaConsulVersion]; !ok && ns.ID == "consul" && ns.Meta["version"] != "" { + if dump.Meta == nil { + dump.Meta = make(map[string]string) + } + dump.Meta[structs.MetaConsulVersion] = ns.Meta["version"] + } dump.Services = append(dump.Services, ns) } diff --git a/agent/consul/state/catalog_test.go b/agent/consul/state/catalog_test.go index 0de535c3b4b3..e6b279580b03 100644 --- a/agent/consul/state/catalog_test.go +++ b/agent/consul/state/catalog_test.go @@ -4837,6 +4837,9 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { } // Register some nodes + // node1 is registered withOut any nodemeta, and a consul service with id + // 'consul' is added later with meta 'version'. The expected node must have + // meta 'consul-version' with same value testRegisterNode(t, s, 0, "node1") testRegisterNode(t, s, 1, "node2") @@ -4845,6 +4848,8 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { testRegisterService(t, s, 3, "node1", "service2") testRegisterService(t, s, 4, "node2", "service1") testRegisterService(t, s, 5, "node2", "service2") + // Register consul service with meta 'version' for node1 + testRegisterServiceWithMeta(t, s, 10, "node1", "consul", map[string]string{"version": "1.17.0"}) // Register service-level checks testRegisterCheck(t, s, 6, "node1", "service1", "check1", api.HealthPassing) @@ -4894,6 +4899,19 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { }, }, Services: []*structs.NodeService{ + { + ID: "consul", + Service: "consul", + Address: "1.1.1.1", + Meta: map[string]string{"version": "1.17.0"}, + Port: 1111, + Weights: &structs.Weights{Passing: 1, Warning: 1}, + RaftIndex: structs.RaftIndex{ + CreateIndex: 10, + ModifyIndex: 10, + }, + EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), + }, { ID: "service1", Service: "service1", @@ -4921,6 +4939,7 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), }, }, + Meta: map[string]string{"consul-version": "1.17.0"}, }, &structs.NodeInfo{ Node: "node2", @@ -4988,7 +5007,7 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if idx != 9 { + if idx != 10 { t.Fatalf("bad index: %d", idx) } require.Len(t, dump, 1) @@ -4999,8 +5018,8 @@ func TestStateStore_NodeInfo_NodeDump(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if idx != 9 { - t.Fatalf("bad index: %d", 9) + if idx != 10 { + t.Fatalf("bad index: %d", idx) } if !reflect.DeepEqual(dump, expect) { t.Fatalf("bad: %#v", dump[0].Services[0]) diff --git a/agent/consul/state/state_store_test.go b/agent/consul/state/state_store_test.go index fef750253272..587f15c03d94 100644 --- a/agent/consul/state/state_store_test.go +++ b/agent/consul/state/state_store_test.go @@ -189,6 +189,37 @@ func testRegisterServiceWithChangeOpts(t *testing.T, s *Store, idx uint64, nodeI return svc } +// testRegisterServiceWithMeta registers service with Meta passed as arg. +func testRegisterServiceWithMeta(t *testing.T, s *Store, idx uint64, nodeID, serviceID string, meta map[string]string, opts ...func(service *structs.NodeService)) *structs.NodeService { + svc := &structs.NodeService{ + ID: serviceID, + Service: serviceID, + Address: "1.1.1.1", + Port: 1111, + Meta: meta, + } + for _, o := range opts { + o(svc) + } + + if err := s.EnsureService(idx, nodeID, svc); err != nil { + t.Fatalf("err: %s", err) + } + + tx := s.db.Txn(false) + defer tx.Abort() + service, err := tx.First(tableServices, indexID, NodeServiceQuery{Node: nodeID, Service: serviceID, PeerName: svc.PeerName}) + if err != nil { + t.Fatalf("err: %s", err) + } + if result, ok := service.(*structs.ServiceNode); !ok || + result.Node != nodeID || + result.ServiceID != serviceID { + t.Fatalf("bad service: %#v", result) + } + return svc +} + // testRegisterService register a service with given transaction idx // If the service already exists, transaction number might not be increased // Use `testRegisterServiceWithChange()` if you want perform a registration that diff --git a/agent/local/state_test.go b/agent/local/state_test.go index 0a78f321f7a5..4751352ec1c8 100644 --- a/agent/local/state_test.go +++ b/agent/local/state_test.go @@ -189,7 +189,8 @@ func TestAgentAntiEntropy_Services(t *testing.T) { id := services.NodeServices.Node.ID addrs := services.NodeServices.Node.TaggedAddresses meta := services.NodeServices.Node.Meta - delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaConsulVersion) // Added later, not in config. assert.Equal(t, a.Config.NodeID, id) assert.Equal(t, a.Config.TaggedAddresses, addrs) assert.Equal(t, unNilMap(a.Config.NodeMeta), meta) @@ -1355,7 +1356,8 @@ func TestAgentAntiEntropy_Checks(t *testing.T) { id := services.NodeServices.Node.ID addrs := services.NodeServices.Node.TaggedAddresses meta := services.NodeServices.Node.Meta - delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaConsulVersion) // Added later, not in config. assert.Equal(r, a.Config.NodeID, id) assert.Equal(r, a.Config.TaggedAddresses, addrs) assert.Equal(r, unNilMap(a.Config.NodeMeta), meta) @@ -2016,7 +2018,8 @@ func TestAgentAntiEntropy_NodeInfo(t *testing.T) { addrs := services.NodeServices.Node.TaggedAddresses meta := services.NodeServices.Node.Meta nodeLocality := services.NodeServices.Node.Locality - delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaConsulVersion) // Added later, not in config. require.Equal(t, a.Config.NodeID, id) require.Equal(t, a.Config.TaggedAddresses, addrs) require.Equal(t, a.Config.StructLocality(), nodeLocality) @@ -2041,7 +2044,8 @@ func TestAgentAntiEntropy_NodeInfo(t *testing.T) { addrs := services.NodeServices.Node.TaggedAddresses meta := services.NodeServices.Node.Meta nodeLocality := services.NodeServices.Node.Locality - delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaSegmentKey) // Added later, not in config. + delete(meta, structs.MetaConsulVersion) // Added later, not in config. require.Equal(t, nodeID, id) require.Equal(t, a.Config.TaggedAddresses, addrs) require.Equal(t, a.Config.StructLocality(), nodeLocality) diff --git a/api/catalog_test.go b/api/catalog_test.go index 6226691353f2..2b0a4097b332 100644 --- a/api/catalog_test.go +++ b/api/catalog_test.go @@ -65,6 +65,7 @@ func TestAPI_CatalogNodes(t *testing.T) { }, Meta: map[string]string{ "consul-network-segment": "", + "consul-version": s.Config.Version, }, } require.Equal(r, want, got) diff --git a/api/txn_test.go b/api/txn_test.go index 975f3e38163b..ea454976daea 100644 --- a/api/txn_test.go +++ b/api/txn_test.go @@ -361,7 +361,10 @@ func TestAPI_ClientTxn(t *testing.T) { "wan": s.Config.Bind, "wan_ipv4": s.Config.Bind, }, - Meta: map[string]string{"consul-network-segment": ""}, + Meta: map[string]string{ + "consul-network-segment": "", + "consul-version": s.Config.Version, + }, CreateIndex: ret.Results[1].Node.CreateIndex, ModifyIndex: ret.Results[1].Node.ModifyIndex, }, diff --git a/sdk/testutil/server.go b/sdk/testutil/server.go index d00850d5e17a..a20f95123aab 100644 --- a/sdk/testutil/server.go +++ b/sdk/testutil/server.go @@ -130,6 +130,7 @@ type TestServerConfig struct { Args []string `json:"-"` ReturnPorts func() `json:"-"` Audit *TestAuditConfig `json:"audit,omitempty"` + Version string `json:"version,omitempty"` } type TestACLs struct { @@ -212,6 +213,7 @@ func defaultServerConfig(t TestingTB, consulVersion *version.Version) *TestServe Stdout: logBuffer, Stderr: logBuffer, Peering: &TestPeeringConfig{Enabled: true}, + Version: consulVersion.String(), } // Add version-specific tweaks diff --git a/ui/packages/consul-ui/app/components/consul/node/search-bar/index.hbs b/ui/packages/consul-ui/app/components/consul/node/search-bar/index.hbs index 47759b155d6b..e22e6b2b01c6 100644 --- a/ui/packages/consul-ui/app/components/consul/node/search-bar/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/node/search-bar/index.hbs @@ -1,165 +1,139 @@ {{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: MPL-2.0 +Copyright (c) HashiCorp, Inc. +SPDX-License-Identifier: MPL-2.0 }} - - <:status as |search|> + + <:status as |search|> -{{#let + {{#let - (t (concat "components.consul.node.search-bar." search.status.key) - default=(array - (concat "common.search." search.status.key) - (concat "common.consul." search.status.key) - ) - ) + (t (concat "components.consul.node.search-bar." search.status.key) + default=(array + (concat "common.search." search.status.key) + (concat "common.consul." search.status.key) + ) + ) - (if search.status.value - search.status.value + (if search.status.value + search.status.value (t (concat "components.consul.node.search-bar." search.status.value) - default=(array - (concat "common.search." search.status.value) - (concat "common.consul." search.status.value) - (concat "common.brand." search.status.value) - )) - ) + default=(array + (concat "common.search." search.status.value) + (concat "common.consul." search.status.value) + (concat "common.brand." search.status.value) + )) + ) -as |key value|}} - -
-
{{key}}
-
{{value}}
-
-
-{{/let}} - - - <:search as |search|> - - - - - {{t "common.search.searchproperty"}} - - - - {{#let components.Optgroup components.Option as |Optgroup Option|}} - {{#each @filter.searchproperty.default as |prop|}} - - {{/each}} + as |key value|}} + +
+
{{key}}
+
{{value}}
+
+
{{/let}} -
-
-
- - <:filter as |search|> - + + + <:search as |search|> + + - {{t "common.consul.status"}} + {{t "common.search.searchproperty"}} - {{#let components.Optgroup components.Option as |Optgroup Option|}} - {{#each (array "passing" "warning" "critical") as |state|}} - - {{/each}} - {{/let}} + {{/each}} + {{/let}} - + + <:filter as |search|> + + + + {{t "common.consul.status"}} + + + + {{#let components.Optgroup components.Option as |Optgroup Option|}} + {{#each (array "passing" "warning" "critical") as |state|}} + + {{/each}} + {{/let}} + + + - - - {{t "common.consul.version"}} - - - + + + {{t "common.consul.version"}} + + + {{#let components.Optgroup components.Option as |Optgroup Option|}} {{#each @versions as |version|}} - + {{/each}} {{/let}} - - - - <:sort as |search|> - - - - {{#let (from-entries (array - (array "Node:asc" (t "common.sort.alpha.asc")) - (array "Node:desc" (t "common.sort.alpha.desc")) - (array "Status:asc" (t "common.sort.status.asc")) - (array "Status:desc" (t "common.sort.status.desc")) - (array "Version:asc" (t "common.sort.version.asc")) - (array "Version:desc" (t "common.sort.version.desc")) - )) - as |selectable| - }} - {{get selectable @sort.value}} - {{/let}} - - - - {{#let components.Optgroup components.Option as |Optgroup Option|}} - - - - - - - - - - - - - {{/let}} - - - -
\ No newline at end of file + + + + <:sort as |search|> + + + + {{#let (from-entries (array + (array "Node:asc" (t "common.sort.alpha.asc")) + (array "Node:desc" (t "common.sort.alpha.desc")) + (array "Status:asc" (t "common.sort.status.asc")) + (array "Status:desc" (t "common.sort.status.desc")) + (array "Version:asc" (t "common.sort.version.asc")) + (array "Version:desc" (t "common.sort.version.desc")) + )) + as |selectable| + }} + {{get selectable @sort.value}} + {{/let}} + + + + {{#let components.Optgroup components.Option as |Optgroup Option|}} + + + + + + + + + + + + + {{/let}} + + + +