Skip to content

Commit 9e06f93

Browse files
authored
Merge branch 'main' into ddelemeny/refactor-multisearch
2 parents e68dd1f + 84e132e commit 9e06f93

File tree

14 files changed

+371
-272
lines changed

14 files changed

+371
-272
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ Quickwit 0.8 is compatible with 0.4.x versions only.
2929

3030
You can either download the plugin manually and unzip it into the plugin directory or use the env variable `GF_INSTALL_PLUGINS` to install it.
3131

32-
### 0.4.5 for Quickwit 0.8
32+
### 0.4.6 for Quickwit 0.8
3333

3434
Run `grafana-oss` container with the env variable:
3535

3636
```bash
37-
docker run -p 3000:3000 -e GF_INSTALL_PLUGINS="https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.4.5/quickwit-quickwit-datasource-0.4.5.zip;quickwit-quickwit-datasource" grafana/grafana-oss run
37+
docker run -p 3000:3000 -e GF_INSTALL_PLUGINS="https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.4.6/quickwit-quickwit-datasource-0.4.6.zip;quickwit-quickwit-datasource" grafana/grafana-oss run
3838
```
3939

4040
Or download the plugin manually and start Grafana
4141

4242
```bash
43-
wget https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.4.5/quickwit-quickwit-datasource-0.4.5.zip
43+
wget https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.4.6/quickwit-quickwit-datasource-0.4.6.zip
4444
mkdir -p plugins
45-
unzip quickwit-quickwit-datasource-0.4.5.zip -d plugins/quickwit-quickwit-datasource-0.4.5
45+
unzip quickwit-quickwit-datasource-0.4.6.zip -d plugins/quickwit-quickwit-datasource-0.4.6
4646
docker run -p 3000:3000 -e GF_PATHS_PLUGINS=/data/plugins -v ${PWD}/plugins:/data/plugins grafana/grafana-oss run
4747
```
4848

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quickwit-datasource",
3-
"version": "0.4.5",
3+
"version": "0.4.6",
44
"description": "Quickwit datasource",
55
"scripts": {
66
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",

pkg/quickwit/client/client.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@ import (
1515
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
1616
)
1717

18+
type ReadyStatus struct {
19+
IsReady bool
20+
Err error
21+
}
22+
1823
type DatasourceInfo struct {
1924
ID int64
2025
HTTPClient *http.Client
2126
URL string
2227
Database string
2328
ConfiguredFields ConfiguredFields
2429
MaxConcurrentShardRequests int64
30+
ReadyStatus chan ReadyStatus
31+
ShouldInit bool
2532
}
2633

2734
// TODO: Move ConfiguredFields closer to handlers, the client layer doesn't need this stuff

pkg/quickwit/quickwit.go

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
5757
return nil, err
5858
}
5959

60-
timeField, toOk := jsonData["timeField"].(string)
61-
timeOutputFormat, tofOk := jsonData["timeOutputFormat"].(string)
62-
6360
logLevelField, ok := jsonData["logLevelField"].(string)
6461
if !ok {
6562
logLevelField = ""
@@ -74,6 +71,7 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
7471
if !ok {
7572
index = ""
7673
}
74+
// XXX : Legacy check, should not happen ?
7775
if index == "" {
7876
index = settings.Database
7977
}
@@ -92,18 +90,11 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
9290
maxConcurrentShardRequests = 256
9391
}
9492

95-
if !toOk || !tofOk {
96-
timeField, timeOutputFormat, err = GetTimestampFieldInfos(index, settings.URL, httpCli)
97-
if nil != err {
98-
return nil, err
99-
}
100-
}
101-
10293
configuredFields := es.ConfiguredFields{
103-
TimeField: timeField,
104-
TimeOutputFormat: timeOutputFormat,
10594
LogLevelField: logLevelField,
10695
LogMessageField: logMessageField,
96+
TimeField: "",
97+
TimeOutputFormat: "",
10798
}
10899

109100
model := es.DatasourceInfo{
@@ -113,16 +104,62 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
113104
Database: index,
114105
MaxConcurrentShardRequests: int64(maxConcurrentShardRequests),
115106
ConfiguredFields: configuredFields,
107+
ReadyStatus: make(chan es.ReadyStatus, 1),
108+
ShouldInit: true,
116109
}
117-
return &QuickwitDatasource{dsInfo: model}, nil
110+
111+
ds := &QuickwitDatasource{dsInfo: model}
112+
113+
// Create an initialization goroutine
114+
go func(ds *QuickwitDatasource, readyStatus chan<- es.ReadyStatus) {
115+
var status es.ReadyStatus = es.ReadyStatus{
116+
IsReady: false,
117+
Err: nil,
118+
}
119+
for {
120+
// Will retry init everytime the channel is consumed until ready
121+
if !status.IsReady || ds.dsInfo.ShouldInit {
122+
qwlog.Debug("Initializing Datasource")
123+
status.IsReady = true
124+
status.Err = nil
125+
126+
indexMetadataList, err := GetIndexesMetadata(ds.dsInfo.Database, ds.dsInfo.URL, ds.dsInfo.HTTPClient)
127+
if err != nil {
128+
status.IsReady = false
129+
status.Err = fmt.Errorf("failed to get index metadata : %w", err)
130+
} else if len(indexMetadataList) == 0 {
131+
status.IsReady = false
132+
status.Err = fmt.Errorf("no index found for %s", ds.dsInfo.Database)
133+
} else {
134+
timeField, timeOutputFormat, err := GetTimestampFieldInfos(indexMetadataList)
135+
if nil != err {
136+
status.IsReady = false
137+
status.Err = err
138+
} else if "" == timeField {
139+
status.IsReady = false
140+
status.Err = fmt.Errorf("timefield is empty for %s", ds.dsInfo.Database)
141+
} else if "" == timeOutputFormat {
142+
status.Err = fmt.Errorf("timefield's output_format is empty, logs timestamps will not be parsed correctly for %s", ds.dsInfo.Database)
143+
}
144+
145+
ds.dsInfo.ConfiguredFields.TimeField = timeField
146+
ds.dsInfo.ConfiguredFields.TimeOutputFormat = timeOutputFormat
147+
ds.dsInfo.ShouldInit = false
148+
}
149+
}
150+
readyStatus <- status
151+
}
152+
}(ds, model.ReadyStatus)
153+
return ds, nil
118154
}
119155

120156
// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
121157
// created. As soon as datasource settings change detected by SDK old datasource instance will
122158
// be disposed and a new one will be created using NewSampleDatasource factory function.
123159
func (ds *QuickwitDatasource) Dispose() {
124-
// Clean up datasource instance resources.
125-
// TODO
160+
// FIXME: The ReadyStatus channel should probably be closed here, but doing it
161+
// causes odd calls to healthcheck to fail. Needs investigation
162+
// close(ds.dsInfo.ReadyStatus)
126163
}
127164

128165
// CheckHealth handles health checks sent from Grafana to the plugin.
@@ -131,13 +168,39 @@ func (ds *QuickwitDatasource) Dispose() {
131168
// a datasource is working as expected.
132169
func (ds *QuickwitDatasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
133170
res := &backend.CheckHealthResult{}
134-
135171
res.Status = backend.HealthStatusOk
136172
res.Message = "plugin is running"
173+
174+
ds.dsInfo.ShouldInit = true
175+
status := <-ds.dsInfo.ReadyStatus
176+
177+
if nil != status.Err {
178+
res.Status = backend.HealthStatusError
179+
res.Message = fmt.Errorf("Failed to initialize datasource: %w", status.Err).Error()
180+
} else if "" == ds.dsInfo.ConfiguredFields.TimeField {
181+
res.Status = backend.HealthStatusError
182+
res.Message = fmt.Sprintf("timefield is missing from index config \"%s\"", ds.dsInfo.Database)
183+
} else if "" == ds.dsInfo.ConfiguredFields.TimeOutputFormat {
184+
res.Status = backend.HealthStatusError
185+
res.Message = fmt.Sprintf("timefield's output_format is missing from index config \"%s\"", ds.dsInfo.Database)
186+
}
187+
qwlog.Debug(res.Message)
188+
137189
return res, nil
138190
}
139191

140192
func (ds *QuickwitDatasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
193+
// Ensure ds is initialized, we need timestamp infos
194+
status := <-ds.dsInfo.ReadyStatus
195+
if !status.IsReady {
196+
qwlog.Debug(fmt.Errorf("Datasource initialization failed: %w", status.Err).Error())
197+
response := &backend.QueryDataResponse{
198+
Responses: backend.Responses{},
199+
}
200+
response.Responses["__qwQueryDataError"] = backend.ErrDataResponse(backend.StatusInternal, "Datasource initialization failed")
201+
return response, nil
202+
}
203+
141204
return queryData(ctx, req.Queries, &ds.dsInfo)
142205
}
143206

0 commit comments

Comments
 (0)