Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: include nodes count in operator usage endpoint and cli command #17939

Merged
merged 2 commits into from Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/17939.txt
@@ -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
```
6 changes: 6 additions & 0 deletions agent/consul/state/usage.go
Expand Up @@ -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)
}

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)
for _, kind := range allConnectKind {
usage, err := firstUsageEntry(ws, tx, connectUsageTableName(kind))
Expand All @@ -443,6 +448,7 @@ func (s *Store) ServiceUsage(ws memdb.WatchSet) (uint64, structs.ServiceUsage, e
Services: services.Count,
ConnectServiceInstances: serviceKindInstances,
BillableServiceInstances: billableServiceInstances.Count,
Nodes: nodes.Count,
}
results, err := compileEnterpriseServiceUsage(ws, tx, usage)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions agent/operator_endpoint_oss_test.go
Expand Up @@ -65,6 +65,7 @@ func TestOperator_Usage(t *testing.T) {
},
// 4 = 6 total service instances - 1 connect proxy - 1 consul service
BillableServiceInstances: 4,
Nodes: 2,
},
}
require.Equal(t, expected, raw.(structs.Usage).Usage)
Expand Down
1 change: 1 addition & 0 deletions agent/structs/structs.go
Expand Up @@ -2324,6 +2324,7 @@ type ServiceUsage struct {
ServiceInstances int
ConnectServiceInstances map[string]int
BillableServiceInstances int
Nodes int
EnterpriseServiceUsage
}

Expand Down
1 change: 1 addition & 0 deletions api/operator_usage.go
Expand Up @@ -10,6 +10,7 @@ type Usage struct {

// ServiceUsage contains information about the number of services and service instances for a datacenter.
type ServiceUsage struct {
Nodes int
Services int
ServiceInstances int
ConnectServiceInstances map[string]int
Expand Down
36 changes: 36 additions & 0 deletions command/operator/usage/instances/usage_instances.go
Expand Up @@ -99,6 +99,14 @@ func (c *cmd) Run(args []string) int {
return 1
}
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")
JadhavPoonam marked this conversation as resolved.
Show resolved Hide resolved
}

// Output Connect service counts
Expand All @@ -115,6 +123,34 @@ func (c *cmd) Run(args []string) int {
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) {
var output bytes.Buffer
tw := tabwriter.NewWriter(&output, 0, 2, 6, ' ', 0)
Expand Down
51 changes: 51 additions & 0 deletions command/operator/usage/instances/usage_instances_oss_test.go
Expand Up @@ -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)
})
}
}
3 changes: 2 additions & 1 deletion website/content/api-docs/operator/usage.mdx
Expand Up @@ -64,7 +64,8 @@ $ curl \
"mesh-gateway": 0,
"terminating-gateway": 0
},
"BillableServiceInstances": 0
"BillableServiceInstances": 0,
"Nodes": 1
}
},
"Index": 13,
Expand Down
13 changes: 13 additions & 0 deletions website/content/commands/operator/usage.mdx
Expand Up @@ -50,6 +50,12 @@ Billable Services
Services Service instances
2 3

Nodes
Datacenter Count
dc1 1

Total 1

Connect Services
Type Service instances
connect-native 0
Expand All @@ -74,6 +80,13 @@ dc2 1 1

Total 3 4

Nodes
Datacenter Count
dc1 1
dc2 2

Total 3

Connect Services
Datacenter Type Service instances
dc1 connect-native 0
Expand Down