Backport of feat: include nodes count in operator usage endpoint and cli command into release/1.16.x (#18012)

* backport of commit 54cdccd019ce32227f679b3fdca499283fdbdf5e

* backport of commit e543f716937fabed12ae2872d242a99416846d86

---------

Co-authored-by: Poonam Jadhav <poonam.jadhav@hashicorp.com>
This commit is contained in:
hc-github-team-consul-core 2023-07-05 10:37:51 -05:00 committed by GitHub
parent 3dcc3cb95a
commit 4045bcfef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 115 additions and 1 deletions

4
.changelog/17939.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:improvement
http: GET API `operator/usage` endpoint now returns node count
cli: `consul operator usage` command now returns node count
```

View File

@ -424,6 +424,11 @@ func (s *Store) ServiceUsage(ws memdb.WatchSet) (uint64, structs.ServiceUsage, e
return 0, structs.ServiceUsage{}, fmt.Errorf("failed services lookup: %s", err) return 0, structs.ServiceUsage{}, fmt.Errorf("failed services lookup: %s", err)
} }
nodes, err := firstUsageEntry(ws, tx, tableNodes)
if err != nil {
return 0, structs.ServiceUsage{}, fmt.Errorf("failed nodes lookup: %s", err)
}
serviceKindInstances := make(map[string]int) serviceKindInstances := make(map[string]int)
for _, kind := range allConnectKind { for _, kind := range allConnectKind {
usage, err := firstUsageEntry(ws, tx, connectUsageTableName(kind)) usage, err := firstUsageEntry(ws, tx, connectUsageTableName(kind))
@ -443,6 +448,7 @@ func (s *Store) ServiceUsage(ws memdb.WatchSet) (uint64, structs.ServiceUsage, e
Services: services.Count, Services: services.Count,
ConnectServiceInstances: serviceKindInstances, ConnectServiceInstances: serviceKindInstances,
BillableServiceInstances: billableServiceInstances.Count, BillableServiceInstances: billableServiceInstances.Count,
Nodes: nodes.Count,
} }
results, err := compileEnterpriseServiceUsage(ws, tx, usage) results, err := compileEnterpriseServiceUsage(ws, tx, usage)
if err != nil { if err != nil {

View File

@ -65,6 +65,7 @@ func TestOperator_Usage(t *testing.T) {
}, },
// 4 = 6 total service instances - 1 connect proxy - 1 consul service // 4 = 6 total service instances - 1 connect proxy - 1 consul service
BillableServiceInstances: 4, BillableServiceInstances: 4,
Nodes: 2,
}, },
} }
require.Equal(t, expected, raw.(structs.Usage).Usage) require.Equal(t, expected, raw.(structs.Usage).Usage)

View File

@ -2308,6 +2308,7 @@ type ServiceUsage struct {
ServiceInstances int ServiceInstances int
ConnectServiceInstances map[string]int ConnectServiceInstances map[string]int
BillableServiceInstances int BillableServiceInstances int
Nodes int
EnterpriseServiceUsage EnterpriseServiceUsage
} }

View File

@ -10,6 +10,7 @@ type Usage struct {
// ServiceUsage contains information about the number of services and service instances for a datacenter. // ServiceUsage contains information about the number of services and service instances for a datacenter.
type ServiceUsage struct { type ServiceUsage struct {
Nodes int
Services int Services int
ServiceInstances int ServiceInstances int
ConnectServiceInstances map[string]int ConnectServiceInstances map[string]int

View File

@ -99,6 +99,14 @@ func (c *cmd) Run(args []string) int {
return 1 return 1
} }
c.UI.Output(billableOutput + "\n") c.UI.Output(billableOutput + "\n")
c.UI.Output("\nNodes")
nodesOutput, err := formatNodesCounts(usage.Usage)
if err != nil {
c.UI.Error(err.Error())
return 1
}
c.UI.Output(nodesOutput + "\n\n")
} }
// Output Connect service counts // Output Connect service counts
@ -115,6 +123,34 @@ func (c *cmd) Run(args []string) int {
return 0 return 0
} }
func formatNodesCounts(usageStats map[string]api.ServiceUsage) (string, error) {
var output bytes.Buffer
tw := tabwriter.NewWriter(&output, 0, 2, 6, ' ', 0)
nodesTotal := 0
fmt.Fprintf(tw, "Datacenter\t")
fmt.Fprintf(tw, "Count\t")
fmt.Fprint(tw, "\t\n")
for dc, usage := range usageStats {
nodesTotal += usage.Nodes
fmt.Fprintf(tw, "%s\t%d\n", dc, usage.Nodes)
}
fmt.Fprint(tw, "\t\n")
fmt.Fprintf(tw, "Total")
fmt.Fprintf(tw, "\t%d", nodesTotal)
if err := tw.Flush(); err != nil {
return "", fmt.Errorf("Error flushing tabwriter: %s", err)
}
return strings.TrimSpace(output.String()), nil
}
func formatServiceCounts(usageStats map[string]api.ServiceUsage, billable, showDatacenter bool) (string, error) { func formatServiceCounts(usageStats map[string]api.ServiceUsage, billable, showDatacenter bool) (string, error) {
var output bytes.Buffer var output bytes.Buffer
tw := tabwriter.NewWriter(&output, 0, 2, 6, ' ', 0) tw := tabwriter.NewWriter(&output, 0, 2, 6, ' ', 0)

View File

@ -117,3 +117,54 @@ Total 45`,
}) })
} }
} }
func TestUsageInstances_formatNodesCounts(t *testing.T) {
usageBasic := map[string]api.ServiceUsage{
"dc1": {
Nodes: 10,
},
}
usageMultiDC := map[string]api.ServiceUsage{
"dc1": {
Nodes: 10,
},
"dc2": {
Nodes: 11,
},
}
cases := []struct {
name string
usageStats map[string]api.ServiceUsage
expectedNodes string
}{
{
name: "basic",
usageStats: usageBasic,
expectedNodes: `
Datacenter Count
dc1 10
Total 10`,
},
{
name: "multi-datacenter",
usageStats: usageMultiDC,
expectedNodes: `
Datacenter Count
dc1 10
dc2 11
Total 21`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
nodesOutput, err := formatNodesCounts(tc.usageStats)
require.NoError(t, err)
require.Equal(t, strings.TrimSpace(tc.expectedNodes), nodesOutput)
})
}
}

View File

@ -64,7 +64,8 @@ $ curl \
"mesh-gateway": 0, "mesh-gateway": 0,
"terminating-gateway": 0 "terminating-gateway": 0
}, },
"BillableServiceInstances": 0 "BillableServiceInstances": 0,
"Nodes": 1
} }
}, },
"Index": 13, "Index": 13,

View File

@ -50,6 +50,12 @@ Billable Services
Services Service instances Services Service instances
2 3 2 3
Nodes
Datacenter Count
dc1 1
Total 1
Connect Services Connect Services
Type Service instances Type Service instances
connect-native 0 connect-native 0
@ -74,6 +80,13 @@ dc2 1 1
Total 3 4 Total 3 4
Nodes
Datacenter Count
dc1 1
dc2 2
Total 3
Connect Services Connect Services
Datacenter Type Service instances Datacenter Type Service instances
dc1 connect-native 0 dc1 connect-native 0