Skip to content

Commit 3bce784

Browse files
authored
Metrics tool supports bandwidth usage (#34)
1 parent 3f04efd commit 3bce784

File tree

5 files changed

+61
-5
lines changed

5 files changed

+61
-5
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ feedback or would like to report a bug or feature request, please [create a GitH
220220

221221
- **get_metrics** - Get performance metrics for any Render resource (services, Postgres databases, key-value stores). Metrics may be empty if the metric is not valid for the given resource
222222
- `resourceId`: The ID of the resource to get metrics for (service ID, Postgres ID, or key-value store ID) (string, required)
223-
- `metricTypes`: Which metrics to fetch (array of strings, required). Accepted values: 'cpu_usage', 'cpu_limit', 'cpu_target', 'memory_usage', 'memory_limit', 'memory_target', 'http_request_count', 'active_connections', 'instance_count', 'http_latency'. CPU usage/limits/targets, memory usage/limits/targets, and instance count metrics are available for all resources. HTTP request counts and response time metrics are only available for services. Active connection metrics are only available for databases and key-value stores. Limits show resource constraints, targets show autoscaling thresholds
223+
- `metricTypes`: Which metrics to fetch (array of strings, required). Accepted values: 'cpu_usage', 'cpu_limit', 'cpu_target', 'memory_usage', 'memory_limit', 'memory_target', 'http_request_count', 'active_connections', 'instance_count', 'http_latency', 'bandwidth_usage'. CPU usage/limits/targets, memory usage/limits/targets, and instance count metrics are available for all resources. HTTP request counts and response time metrics, and bandwidth usage metrics are only available for services. Active connection metrics are only available for databases and key-value stores. Limits show resource constraints, targets show autoscaling thresholds
224224
- `startTime`: Start time for metrics query in RFC3339 format (e.g., '2024-01-01T12:00:00Z'), defaults to 1 hour ago. The start time must be within the last 30 days (string, optional)
225225
- `endTime`: End time for metrics query in RFC3339 format (e.g., '2024-01-01T13:00:00Z'), defaults to the current time. The end time must be within the last 30 days (string, optional)
226226
- `resolution`: Time resolution for data points in seconds. Lower values provide more granular data. Higher values provide more aggregated data points. API defaults to 60 seconds if not provided, minimum 30 seconds (number, optional)

pkg/metrics/repo.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type MetricsClient interface {
2121
GetCpuTargetWithResponse(ctx context.Context, params *client.GetCpuTargetParams, reqEditors ...client.RequestEditorFn) (*client.GetCpuTargetResponse, error)
2222
GetMemoryLimitWithResponse(ctx context.Context, params *client.GetMemoryLimitParams, reqEditors ...client.RequestEditorFn) (*client.GetMemoryLimitResponse, error)
2323
GetMemoryTargetWithResponse(ctx context.Context, params *client.GetMemoryTargetParams, reqEditors ...client.RequestEditorFn) (*client.GetMemoryTargetResponse, error)
24+
GetBandwidthWithResponse(ctx context.Context, params *client.GetBandwidthParams, reqEditors ...client.RequestEditorFn) (*client.GetBandwidthResponse, error)
2425
}
2526

2627
type Repo struct {
@@ -44,6 +45,7 @@ const (
4445
MetricTypeCPUTarget MetricType = "cpu_target"
4546
MetricTypeMemoryLimit MetricType = "memory_limit"
4647
MetricTypeMemoryTarget MetricType = "memory_target"
48+
MetricTypeBandwidthUsage MetricType = "bandwidth_usage"
4749
)
4850

4951
type MetricsRequest struct {
@@ -124,6 +126,8 @@ func (r *Repo) fetchMetric(ctx context.Context, resourceId string, metricType Me
124126
data, err = r.getMemoryLimit(ctx, resourceId, req)
125127
case MetricTypeMemoryTarget:
126128
data, err = r.getMemoryTarget(ctx, resourceId, req)
129+
case MetricTypeBandwidthUsage:
130+
data, err = r.getBandwidthUsage(ctx, resourceId, req)
127131
default:
128132
return MetricData{}, fmt.Errorf("unsupported metric type: %s", metricType)
129133
}
@@ -483,3 +487,29 @@ func (r *Repo) getMemoryTarget(ctx context.Context, resourceId string, req Metri
483487

484488
return *resp.JSON200, nil
485489
}
490+
491+
func (r *Repo) getBandwidthUsage(ctx context.Context, resourceId string, req MetricsRequest) (metricstypes.TimeSeriesCollection, error) {
492+
params := &client.GetBandwidthParams{
493+
StartTime: req.StartTime,
494+
EndTime: req.EndTime,
495+
}
496+
497+
// Set resource parameter - use ServiceResourceQueryParam for bandwidth
498+
serviceResource := metricstypes.ServiceResourceQueryParam(resourceId)
499+
params.Resource = &serviceResource
500+
501+
resp, err := r.client.GetBandwidthWithResponse(ctx, params)
502+
if err != nil {
503+
return nil, fmt.Errorf("failed to get bandwidth usage metrics: %w", err)
504+
}
505+
506+
if resp.StatusCode() != 200 {
507+
return nil, fmt.Errorf("bandwidth usage metrics API returned status %d", resp.StatusCode())
508+
}
509+
510+
if resp.JSON200 == nil {
511+
return metricstypes.TimeSeriesCollection{}, nil
512+
}
513+
514+
return *resp.JSON200, nil
515+
}

pkg/metrics/test_helpers.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ func (m *MockClientWithResponses) GetMemoryTargetWithResponse(ctx context.Contex
7272
return args.Get(0).(*client.GetMemoryTargetResponse), args.Error(1)
7373
}
7474

75+
func (m *MockClientWithResponses) GetBandwidthWithResponse(ctx context.Context, params *client.GetBandwidthParams, reqEditors ...client.RequestEditorFn) (*client.GetBandwidthResponse, error) {
76+
args := m.Called(ctx, params, reqEditors)
77+
return args.Get(0).(*client.GetBandwidthResponse), args.Error(1)
78+
}
79+
7580
// MetricsTestSuite provides shared setup and utilities for metrics tests
7681
type MetricsTestSuite struct {
7782
suite.Suite
@@ -232,6 +237,17 @@ func NewMockMemoryTargetResponse(value float32) *client.GetMemoryTargetResponse
232237
}
233238
}
234239

240+
func NewMockBandwidthResponse(value float32) *client.GetBandwidthResponse {
241+
return &client.GetBandwidthResponse{
242+
HTTPResponse: &http.Response{StatusCode: 200},
243+
JSON200: &metricstypes.TimeSeriesCollection{{
244+
Unit: "bytes",
245+
Labels: []metricstypes.Label{{Field: "instance", Value: "srv-123-abc"}},
246+
Values: []metricstypes.TimeSeriesValue{{Timestamp: testTimestamp, Value: value}},
247+
}},
248+
}
249+
}
250+
235251
func NewMockErrorResponse(statusCode int) *http.Response {
236252
return &http.Response{StatusCode: statusCode}
237253
}

pkg/metrics/tools.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func getMetrics(metricsRepo *Repo) server.ServerTool {
2626
return server.ServerTool{
2727
Tool: mcp.NewTool("get_metrics",
2828
mcp.WithDescription("Get performance metrics for any Render resource (services, Postgres databases, key-value stores). "+
29-
"Supports CPU usage/limits/targets, memory usage/limits/targets, service instance counts, HTTP request counts and response time metrics, database active connection counts for debugging, capacity planning, and performance optimization. "+
29+
"Supports CPU usage/limits/targets, memory usage/limits/targets, service instance counts, HTTP request counts and response time metrics, bandwidth usage metrics, database active connection counts for debugging, capacity planning, and performance optimization. "+
3030
"Returns time-series data with timestamps and values for the specified time range. "+
3131
"HTTP metrics support filtering by host and path for more granular analysis. "+
3232
"Limits and targets help understand resource constraints and autoscaling thresholds. "+
@@ -44,7 +44,7 @@ func getMetrics(metricsRepo *Repo) server.ServerTool {
4444
mcp.Required(),
4545
mcp.Description("Which metrics to fetch. "+
4646
"CPU usage/limits/targets, memory usage/limits/targets, and instance count metrics are available for all resources. "+
47-
"HTTP request counts and response time metrics are only available for services. "+
47+
"HTTP request counts and response time metrics, and bandwidth usage metrics are only available for services. "+
4848
"Active connection metrics are only available for databases and key-value stores. "+
4949
"Limits show resource constraints, targets show autoscaling thresholds."),
5050
mcp.Items(map[string]interface{}{
@@ -55,6 +55,7 @@ func getMetrics(metricsRepo *Repo) server.ServerTool {
5555
string(MetricTypeInstanceCount), string(MetricTypeHTTPLatency),
5656
string(MetricTypeCPULimit), string(MetricTypeCPUTarget),
5757
string(MetricTypeMemoryLimit), string(MetricTypeMemoryTarget),
58+
string(MetricTypeBandwidthUsage),
5859
},
5960
}),
6061
),
@@ -133,10 +134,10 @@ func getMetrics(metricsRepo *Repo) server.ServerTool {
133134

134135
metricType := MetricType(mtStr)
135136
switch metricType {
136-
case MetricTypeCPUUsage, MetricTypeMemoryUsage, MetricTypeHTTPRequestCount, MetricTypeActiveConnections, MetricTypeInstanceCount, MetricTypeHTTPLatency, MetricTypeCPULimit, MetricTypeCPUTarget, MetricTypeMemoryLimit, MetricTypeMemoryTarget:
137+
case MetricTypeCPUUsage, MetricTypeMemoryUsage, MetricTypeHTTPRequestCount, MetricTypeActiveConnections, MetricTypeInstanceCount, MetricTypeHTTPLatency, MetricTypeCPULimit, MetricTypeCPUTarget, MetricTypeMemoryLimit, MetricTypeMemoryTarget, MetricTypeBandwidthUsage:
137138
metricTypes = append(metricTypes, metricType)
138139
default:
139-
return mcp.NewToolResultError(fmt.Sprintf("invalid metric type: %s. Must be one of: cpu_usage, memory_usage, http_request_count, active_connections, instance_count, http_latency, cpu_limit, cpu_target, memory_limit, memory_target", mtStr)), nil
140+
return mcp.NewToolResultError(fmt.Sprintf("invalid metric type: %s. Must be one of: cpu_usage, memory_usage, http_request_count, active_connections, instance_count, http_latency, cpu_limit, cpu_target, memory_limit, memory_target, bandwidth_usage", mtStr)), nil
140141
}
141142
}
142143

pkg/metrics/tools_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ func (s *MetricsIntegrationSuite) TestSuccessfulMetricsFetching() {
121121
},
122122
value: 1073741824,
123123
},
124+
{
125+
name: "Bandwidth usage success",
126+
metricType: MetricTypeBandwidthUsage,
127+
setupMock: func() {
128+
s.mockClient.On("GetBandwidthWithResponse", mock.Anything, mock.Anything, mock.Anything).
129+
Return(NewMockBandwidthResponse(1048576), nil)
130+
},
131+
value: 1048576,
132+
},
124133
}
125134

126135
for _, tt := range tests {

0 commit comments

Comments
 (0)