Skip to content

Commit

Permalink
recommender: fix instrumentation and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
iksaif committed Nov 7, 2024
1 parent 61e8a7a commit f532ca5
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 16 deletions.
11 changes: 7 additions & 4 deletions controllers/datadoghq/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ const (
transitionPromLabel = "transition"
lifecycleStatus = "lifecycle_status"
monitorName = "monitor_name"
monitorNamespace = "monotor_namespace"
monitorNamespace = "monitor_namespace"
clientPromLabel = "client"
methodPromLabel = "method"
codePromLabel = "code"
// Label values
downscaleCappingPromLabelVal = "downscale_capping"
upscaleCappingPromLabelVal = "upscale_capping"
Expand Down Expand Up @@ -263,19 +266,19 @@ var (
Help: "Tracks the latencies for HTTP requests.",
Buckets: []float64{0.1, 0.3, 0.6, 1, 3, 6, 9, 20},
},
[]string{"client", "method", "code"},
[]string{clientPromLabel, methodPromLabel, codePromLabel},
)
requestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_client_requests_total",
Help: "Tracks the number of HTTP requests.",
}, []string{"client", "method", "code"},
}, []string{clientPromLabel, methodPromLabel, codePromLabel},
)
responseInflight = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "http_client_requests_inflight",
Help: "Tracks the number of client requests currently in progress.",
}, []string{"client"},
}, []string{clientPromLabel},
)
)

Expand Down
22 changes: 11 additions & 11 deletions controllers/datadoghq/recommender.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (r *RecommenderClientImpl) instrumentedClient(recommender string) *http.Cli
}

func instrumentRoundTripper(recommender string, transport http.RoundTripper) http.RoundTripper {
labels := prometheus.Labels{"recommender": recommender}
labels := prometheus.Labels{clientPromLabel: recommender}

return promhttp.InstrumentRoundTripperCounter(
requestsTotal.MustCurryWith(labels),
Expand All @@ -103,26 +103,26 @@ func instrumentRoundTripper(recommender string, transport http.RoundTripper) htt
func (r *RecommenderClientImpl) GetReplicaRecommendation(request *ReplicaRecommendationRequest) (*ReplicaRecommendationResponse, error) {
reco := request.Recommender
if reco == nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("recommender spec is required")
return nil, fmt.Errorf("recommender spec is required")
}

u, err := url.Parse(reco.URL)
if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error parsing url: %w", err)
return nil, fmt.Errorf("error parsing url: %w", err)
}

if u.Scheme != "http" && u.Scheme != "https" {
return &ReplicaRecommendationResponse{}, fmt.Errorf("only http and https schemes are supported")
return nil, fmt.Errorf("only http and https schemes are supported")
}

req, err := buildWorkloadRecommendationRequest(request)
if err != nil {
return &ReplicaRecommendationResponse{}, err
return nil, err
}

payload, err := protojson.Marshal(req)
if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error marshaling request: %w", err)
return nil, fmt.Errorf("error marshaling request: %w", err)
}

// TODO: We might want to make the timeout configurable later.
Expand All @@ -136,7 +136,7 @@ func (r *RecommenderClientImpl) GetReplicaRecommendation(request *ReplicaRecomme
httpReq.Header.Set("User-Agent", "wpa-controller")

if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error creating request: %w", err)
return nil, fmt.Errorf("error creating request: %w", err)
}
resp, err := client.Do(httpReq)

Expand All @@ -147,22 +147,22 @@ func (r *RecommenderClientImpl) GetReplicaRecommendation(request *ReplicaRecomme
}()

if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error sending request: %w", err)
return nil, fmt.Errorf("error sending request: %w", err)
}

if resp.StatusCode != http.StatusOK {
return &ReplicaRecommendationResponse{}, fmt.Errorf("unexpected response code: %d", resp.StatusCode)
return nil, fmt.Errorf("unexpected response code: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error reading response: %w", err)
return nil, fmt.Errorf("error reading response: %w", err)
}

reply := &autoscaling.WorkloadRecommendationReply{}
err = protojson.Unmarshal(body, reply)
if err != nil {
return &ReplicaRecommendationResponse{}, fmt.Errorf("error unmarshaling response: %w", err)
return nil, fmt.Errorf("error unmarshaling response: %w", err)
}

return buildReplicaRecommendationResponse(reply)
Expand Down
24 changes: 23 additions & 1 deletion controllers/datadoghq/recommender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@

package datadoghq

import "time"
import (
"net/http"
"testing"
"time"

"github.com/stretchr/testify/require"
)

func NewMockRecommenderClient() *RecommenderClientMock {
return &RecommenderClientMock{
Expand All @@ -24,3 +30,19 @@ type RecommenderClientMock struct {
ReturnedResponse ReplicaRecommendationResponse
Error error
}

// TODO: Add more tests for the HTTP client
func TestRecommenderClient(t *testing.T) {
client := NewRecommenderClient(http.DefaultClient)
// This should not work with empty requests.
resp, err := client.GetReplicaRecommendation(&ReplicaRecommendationRequest{})
require.Error(t, err)
require.Nil(t, resp)
}

func TestInstrumentation(t *testing.T) {
client := http.DefaultClient
client.Transport = instrumentRoundTripper("http://test", http.DefaultTransport)
// This simply makes sure the instrumentation does crash.
_, _ = client.Get("fake")

Check failure on line 47 in controllers/datadoghq/recommender_test.go

View workflow job for this annotation

GitHub Actions / e2e (v1.14.10)

response body must be closed (bodyclose)

Check failure on line 47 in controllers/datadoghq/recommender_test.go

View workflow job for this annotation

GitHub Actions / build

response body must be closed (bodyclose)

Check failure on line 47 in controllers/datadoghq/recommender_test.go

View workflow job for this annotation

GitHub Actions / e2e (v1.16.9)

response body must be closed (bodyclose)

Check failure on line 47 in controllers/datadoghq/recommender_test.go

View workflow job for this annotation

GitHub Actions / e2e (v1.18.4)

response body must be closed (bodyclose)

Check failure on line 47 in controllers/datadoghq/recommender_test.go

View workflow job for this annotation

GitHub Actions / e2e (v1.21.1)

response body must be closed (bodyclose)
}

0 comments on commit f532ca5

Please sign in to comment.