Skip to content

Commit 3347ed0

Browse files
authored
Merge pull request #853 from Altinity/fix_grafana_reject
Fix grafana rejects
2 parents 621879b + f38071f commit 3347ed0

File tree

7 files changed

+63
-50
lines changed

7 files changed

+63
-50
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ instrumented
77
node_modules
88
venv
99
coverage
10+
.claude*
11+
.qwen*
12+
.opencode*

pkg/client.go

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package main
33
import (
44
"bytes"
55
"context"
6-
"crypto/tls"
7-
"crypto/x509"
86
"encoding/json"
97
"errors"
108
"fmt"
@@ -42,8 +40,6 @@ func (client *ClickHouseClient) Query(ctx context.Context, query string) (*Respo
4240
return onErr(fmt.Errorf("unable to parse clickhouse datasource url: %w", err))
4341
}
4442

45-
httpClient := &http.Client{}
46-
tlsConfig := &tls.Config{}
4743
var req *http.Request
4844
if client.settings.UsePost {
4945
req, err = http.NewRequest("POST", datasourceUrl.String(), bytes.NewBufferString(query))
@@ -87,60 +83,56 @@ func (client *ClickHouseClient) Query(ctx context.Context, query string) (*Respo
8783
}
8884
}
8985

90-
tlsCACert, tlsCACertExists := client.settings.Instance.DecryptedSecureJSONData["tlsCACert"]
91-
tlsClientCert, tlsClientCertExists := client.settings.Instance.DecryptedSecureJSONData["tlsClientCert"]
92-
tlsClientKey, tlsClientKeyExists := client.settings.Instance.DecryptedSecureJSONData["tlsClientKey"]
93-
94-
if tlsCACertExists {
95-
rootCA := x509.NewCertPool()
96-
ok := rootCA.AppendCertsFromPEM([]byte(tlsCACert))
97-
if !ok {
98-
return onErr(errors.New(fmt.Sprintf("invalid tlsCACert: %s", tlsCACert)))
99-
}
100-
tlsConfig.RootCAs = rootCA
101-
}
102-
if tlsClientCertExists != tlsClientKeyExists {
103-
return onErr(errors.New("please setup both tlsClientCert and tlsClientKey"))
104-
}
105-
if tlsClientCertExists && tlsClientKeyExists {
106-
clientKeyPair, err := tls.X509KeyPair([]byte(tlsClientCert), []byte(tlsClientKey))
107-
if err != nil {
108-
return onErr(err)
109-
}
110-
tlsConfig.Certificates = append(tlsConfig.Certificates, clientKeyPair)
111-
}
112-
if client.settings.TLSSkipVerify {
113-
tlsConfig.InsecureSkipVerify = true
114-
}
115-
116-
httpClient.Transport = &http.Transport{TLSClientConfig: tlsConfig}
11786
req = req.WithContext(ctx)
118-
resp, err := httpClient.Do(req)
87+
if client.settings.HTTPClient == nil {
88+
return onErr(errors.New("http client is not initialized"))
89+
}
90+
resp, err := client.settings.HTTPClient.Do(req)
11991
if err != nil {
12092
return onErr(err)
12193
}
94+
defer func() {
95+
if closeErr := resp.Body.Close(); closeErr != nil {
96+
backend.Logger.Warn(fmt.Sprintf("unable to close response body: %v", closeErr))
97+
}
98+
}()
12299

123100
var reader io.Reader
101+
closeEncodedReader := func() {}
124102
switch resp.Header.Get("Content-Encoding") {
125103
case "gzip":
126-
reader, err = gzip.NewReader(resp.Body)
104+
gzipReader, gzipErr := gzip.NewReader(resp.Body)
105+
reader = gzipReader
106+
err = gzipErr
127107
if err != nil {
128108
return onErr(fmt.Errorf("error creating GZIP reader: %v", err))
129109
}
110+
closeEncodedReader = func() {
111+
if closeErr := gzipReader.Close(); closeErr != nil {
112+
backend.Logger.Warn(fmt.Sprintf("unable to close gzip reader: %v", closeErr))
113+
}
114+
}
130115
case "deflate":
131-
reader = flate.NewReader(resp.Body)
116+
flateReader := flate.NewReader(resp.Body)
117+
reader = flateReader
118+
closeEncodedReader = func() {
119+
if closeErr := flateReader.Close(); closeErr != nil {
120+
backend.Logger.Warn(fmt.Sprintf("unable to close deflate reader: %v", closeErr))
121+
}
122+
}
132123
case "br":
133124
reader = brotli.NewReader(resp.Body)
134125
case "zstd":
135126
decoder, zstdErr := zstd.NewReader(resp.Body)
136127
if zstdErr != nil {
137128
return onErr(fmt.Errorf("error creating ZSTD reader: %v", zstdErr))
138129
}
139-
defer decoder.Close()
140130
reader = decoder.IOReadCloser()
131+
closeEncodedReader = decoder.Close
141132
default:
142133
reader = resp.Body
143134
}
135+
defer closeEncodedReader()
144136

145137
body, err := io.ReadAll(reader)
146138
if err != nil {

pkg/datasource_settings.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"net/http"
78
"strings"
89

910
"github.com/grafana/grafana-plugin-sdk-go/backend"
11+
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
1012
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
1113
)
1214

@@ -25,6 +27,7 @@ type DatasourceSettings struct {
2527
TLSSkipVerify bool `json:"tlsSkipVerify"`
2628

2729
CustomHeaders map[string]string `json:"-,omitempty"`
30+
HTTPClient *http.Client `json:"-"`
2831
}
2932

3033
func NewDatasourceSettings(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
@@ -57,8 +60,20 @@ func NewDatasourceSettings(ctx context.Context, settings backend.DataSourceInsta
5760
}
5861

5962
dsSettings.Instance = settings
63+
httpClientOptions, err := settings.HTTPClientOptions(ctx)
64+
if err != nil {
65+
return nil, fmt.Errorf("unable to build http client options: %w", err)
66+
}
67+
dsSettings.HTTPClient, err = httpclient.New(httpClientOptions)
68+
if err != nil {
69+
return nil, fmt.Errorf("unable to create http client: %w", err)
70+
}
6071

6172
return &dsSettings, nil
6273
}
6374

64-
func (s *DatasourceSettings) Dispose() {}
75+
func (s *DatasourceSettings) Dispose() {
76+
if s.HTTPClient != nil {
77+
s.HTTPClient.CloseIdleConnections()
78+
}
79+
}

src/views/QueryEditor/QueryEditor.css

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/views/QueryEditor/QueryEditor.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {useQueryState} from './hooks/useQueryState';
1010
import {useFormattedData} from './hooks/useFormattedData';
1111
import {useAutocompleteData} from './hooks/useAutocompletionData';
1212
import {initializeQueryDefaults, initializeQueryDefaultsForVariables} from './helpers/initializeQueryDefaults';
13-
import './QueryEditor.css';
1413
import {getAdhocFilters} from './helpers/getAdHocFilters';
1514
import {detectVariableMacroIntersections, createVariableMacroConflictWarning} from './helpers/detectVariableMacroIntersections';
1615

src/views/QueryEditor/components/QueryTextEditor/components/AdhocFilterTags/AdhocFilterTags.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import React from 'react';
2+
import { css } from '@emotion/css';
23
import { TagsInput } from '@grafana/ui';
34
import { AdhocFilterTagsProps, AdhocFilter } from '../../types';
45

6+
const tagsInputClassName = css({
7+
margin: '5px 0',
8+
'> div': {
9+
display: 'none',
10+
},
11+
});
12+
513
export const AdhocFilterTags: React.FC<AdhocFilterTagsProps> = ({
614
adhocFilters,
715
areAdHocFiltersAvailable,
@@ -13,7 +21,7 @@ export const AdhocFilterTags: React.FC<AdhocFilterTagsProps> = ({
1321

1422
return (
1523
<TagsInput
16-
className="adhoc-filters-tags"
24+
className={tagsInputClassName}
1725
tags={adhocFilters.map((filter: AdhocFilter) =>
1826
`${filter.key} ${filter.operator} ${filter.value}`
1927
)}

tests/testflows/steps/ui.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,15 @@ def save_coverage(self):
259259
with By("executing 'return JSON.stringify(window.__coverage__);' in chrome console"):
260260
coverage_info = driver.execute_script('return JSON.stringify(window.__coverage__);')
261261

262-
if not(coverage_info is None):
262+
# Skip empty payloads and always write into tests/testflows/coverage/raw
263+
if coverage_info not in (None, "null"):
263264
with By("saving coverage into the file"):
264-
timestamp = datetime.datetime.timestamp(datetime.datetime.now())
265-
file = open(f"coverage/raw/coverage{timestamp}.json", 'w')
266-
file.write(coverage_info)
267-
file.close()
265+
coverage_dir = os.path.join(current_dir(), "..", "coverage", "raw")
266+
os.makedirs(coverage_dir, exist_ok=True)
267+
timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")
268+
file_path = os.path.join(coverage_dir, f"coverage{timestamp}.json")
269+
with open(file_path, "w", encoding="utf-8") as file:
270+
file.write(coverage_info)
268271

269272
@TestStep(When)
270273
def dismiss_alert_if_present(self):

0 commit comments

Comments
 (0)