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:
parent
3dcc3cb95a
commit
4045bcfef7
|
@ -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
|
||||||
|
```
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue