Skip to content

Commit 08e87a2

Browse files
authored
Loki: Add gzip compression to resource calls (grafana#59059)
* Loki: Add compression to `callResource` * add missing tests * fix formatting
1 parent afeb21e commit 08e87a2

File tree

4 files changed

+82
-13
lines changed

4 files changed

+82
-13
lines changed

pkg/tsdb/loki/api.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ type LokiAPI struct {
2424
headers map[string]string
2525
}
2626

27+
type RawLokiResponse struct {
28+
Body []byte
29+
Encoding string
30+
}
31+
2732
func newLokiAPI(client *http.Client, url string, log log.Logger, headers map[string]string) *LokiAPI {
2833
return &LokiAPI{client: client, url: url, log: log, headers: headers}
2934
}
@@ -204,15 +209,15 @@ func makeRawRequest(ctx context.Context, lokiDsUrl string, resourcePath string,
204209
return req, nil
205210
}
206211

207-
func (api *LokiAPI) RawQuery(ctx context.Context, resourcePath string) ([]byte, error) {
212+
func (api *LokiAPI) RawQuery(ctx context.Context, resourcePath string) (RawLokiResponse, error) {
208213
req, err := makeRawRequest(ctx, api.url, resourcePath, api.headers)
209214
if err != nil {
210-
return nil, err
215+
return RawLokiResponse{}, err
211216
}
212217

213218
resp, err := api.client.Do(req)
214219
if err != nil {
215-
return nil, err
220+
return RawLokiResponse{}, err
216221
}
217222

218223
defer func() {
@@ -222,8 +227,18 @@ func (api *LokiAPI) RawQuery(ctx context.Context, resourcePath string) ([]byte,
222227
}()
223228

224229
if resp.StatusCode/100 != 2 {
225-
return nil, makeLokiError(resp.Body)
230+
return RawLokiResponse{}, makeLokiError(resp.Body)
231+
}
232+
233+
body, err := io.ReadAll(resp.Body)
234+
if err != nil {
235+
return RawLokiResponse{}, err
236+
}
237+
238+
encodedBytes := RawLokiResponse{
239+
Body: body,
240+
Encoding: resp.Header.Get("Content-Encoding"),
226241
}
227242

228-
return io.ReadAll(resp.Body)
243+
return encodedBytes, nil
229244
}

pkg/tsdb/loki/api_mock.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,29 @@ func (mockedRT *mockedRoundTripper) RoundTrip(req *http.Request) (*http.Response
3232
}, nil
3333
}
3434

35+
type mockedCompressedRoundTripper struct {
36+
statusCode int
37+
responseBytes []byte
38+
contentType string
39+
requestCallback mockRequestCallback
40+
}
41+
42+
func (mockedRT *mockedCompressedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
43+
requestCallback := mockedRT.requestCallback
44+
if requestCallback != nil {
45+
requestCallback(req)
46+
}
47+
48+
header := http.Header{}
49+
header.Add("Content-Type", mockedRT.contentType)
50+
header.Add("Content-Encoding", "gzip")
51+
return &http.Response{
52+
StatusCode: mockedRT.statusCode,
53+
Header: header,
54+
Body: io.NopCloser(bytes.NewReader(mockedRT.responseBytes)),
55+
}, nil
56+
}
57+
3558
func makeMockedAPI(statusCode int, contentType string, responseBytes []byte, requestCallback mockRequestCallback) *LokiAPI {
3659
return makeMockedAPIWithUrl("http://localhost:9999", statusCode, contentType, responseBytes, requestCallback)
3760
}
@@ -43,3 +66,11 @@ func makeMockedAPIWithUrl(url string, statusCode int, contentType string, respon
4366

4467
return newLokiAPI(&client, url, log.New("test"), nil)
4568
}
69+
70+
func makeCompressedMockedAPIWithUrl(url string, statusCode int, contentType string, responseBytes []byte, requestCallback mockRequestCallback) *LokiAPI {
71+
client := http.Client{
72+
Transport: &mockedCompressedRoundTripper{statusCode: statusCode, contentType: contentType, responseBytes: responseBytes, requestCallback: requestCallback},
73+
}
74+
75+
return newLokiAPI(&client, url, log.New("test"), nil)
76+
}

pkg/tsdb/loki/api_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,18 @@ func TestApiUrlHandling(t *testing.T) {
150150
})
151151
}
152152
}
153+
154+
func TestApiReturnValues(t *testing.T) {
155+
t.Run("Loki should return the right encoding", func(t *testing.T) {
156+
called := false
157+
api := makeCompressedMockedAPIWithUrl("http://localhost:3100", 200, "application/json", []byte("foo"), func(req *http.Request) {
158+
called = true
159+
})
160+
161+
encodedBytes, err := api.RawQuery(context.Background(), "/loki/api/v1/labels?start=1&end=2")
162+
require.NoError(t, err)
163+
require.True(t, called)
164+
require.Equal(t, "gzip", encodedBytes.Encoding)
165+
require.Equal(t, []byte("foo"), encodedBytes.Body)
166+
})
167+
}

pkg/tsdb/loki/loki.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
119119
return callResource(ctx, req, sender, dsInfo, logger.FromContext(ctx))
120120
}
121121

122-
func getAuthHeadersForCallResource(headers map[string][]string) map[string]string {
122+
func getHeadersForCallResource(headers map[string][]string) map[string]string {
123123
data := make(map[string]string)
124124

125125
if auth := arrayHeaderFirstValue(headers["Authorization"]); auth != "" {
@@ -134,6 +134,10 @@ func getAuthHeadersForCallResource(headers map[string][]string) map[string]strin
134134
data["X-ID-Token"] = idToken
135135
}
136136

137+
if encType := arrayHeaderFirstValue(headers["Accept-Encoding"]); encType != "" {
138+
data["Accept-Encoding"] = encType
139+
}
140+
137141
return data
138142
}
139143

@@ -151,19 +155,23 @@ func callResource(ctx context.Context, req *backend.CallResourceRequest, sender
151155
}
152156
lokiURL := fmt.Sprintf("/loki/api/v1/%s", url)
153157

154-
api := newLokiAPI(dsInfo.HTTPClient, dsInfo.URL, plog, getAuthHeadersForCallResource(req.Headers))
155-
bytes, err := api.RawQuery(ctx, lokiURL)
158+
api := newLokiAPI(dsInfo.HTTPClient, dsInfo.URL, plog, getHeadersForCallResource(req.Headers))
159+
encodedBytes, err := api.RawQuery(ctx, lokiURL)
156160

157161
if err != nil {
158162
return err
159163
}
160164

165+
respHeaders := map[string][]string{
166+
"content-type": {"application/json"},
167+
}
168+
if encodedBytes.Encoding != "" {
169+
respHeaders["content-encoding"] = []string{encodedBytes.Encoding}
170+
}
161171
return sender.Send(&backend.CallResourceResponse{
162-
Status: http.StatusOK,
163-
Headers: map[string][]string{
164-
"content-type": {"application/json"},
165-
},
166-
Body: bytes,
172+
Status: http.StatusOK,
173+
Headers: respHeaders,
174+
Body: encodedBytes.Body,
167175
})
168176
}
169177

0 commit comments

Comments
 (0)