mirror of
https://github.com/grafana/grafana.git
synced 2024-11-23 09:26:43 -06:00
Pyroscope: Fix backend panic when querying out of bounds (#76053)
This commit is contained in:
parent
41bcb5e07f
commit
1cbae72a9e
@ -149,6 +149,11 @@ func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Msg.Flamegraph == nil {
|
||||
// Not an error, can happen when querying data oout of range.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
levels := make([]*Level, len(resp.Msg.Flamegraph.Levels))
|
||||
for i, level := range resp.Msg.Flamegraph.Levels {
|
||||
levels[i] = &Level{
|
||||
|
@ -51,10 +51,21 @@ func Test_PyroscopeClient(t *testing.T) {
|
||||
}
|
||||
require.Equal(t, series, resp)
|
||||
})
|
||||
|
||||
t.Run("GetProfile with empty response", func(t *testing.T) {
|
||||
connectClient.SendEmptyProfileResponse = true
|
||||
maxNodes := int64(-1)
|
||||
resp, err := client.GetProfile(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, &maxNodes)
|
||||
require.Nil(t, err)
|
||||
// Mainly ensuring this does not panic like before
|
||||
require.Nil(t, resp)
|
||||
connectClient.SendEmptyProfileResponse = false
|
||||
})
|
||||
}
|
||||
|
||||
type FakePyroscopeConnectClient struct {
|
||||
Req any
|
||||
Req any
|
||||
SendEmptyProfileResponse bool
|
||||
}
|
||||
|
||||
func (f *FakePyroscopeConnectClient) ProfileTypes(ctx context.Context, c *connect.Request[querierv1.ProfileTypesRequest]) (*connect.Response[querierv1.ProfileTypesResponse], error) {
|
||||
@ -75,6 +86,9 @@ func (f *FakePyroscopeConnectClient) Series(ctx context.Context, c *connect.Requ
|
||||
|
||||
func (f *FakePyroscopeConnectClient) SelectMergeStacktraces(ctx context.Context, c *connect.Request[querierv1.SelectMergeStacktracesRequest]) (*connect.Response[querierv1.SelectMergeStacktracesResponse], error) {
|
||||
f.Req = c
|
||||
if f.SendEmptyProfileResponse {
|
||||
return &connect.Response[querierv1.SelectMergeStacktracesResponse]{Msg: &querierv1.SelectMergeStacktracesResponse{}}, nil
|
||||
}
|
||||
return &connect.Response[querierv1.SelectMergeStacktracesResponse]{
|
||||
Msg: &querierv1.SelectMergeStacktracesResponse{
|
||||
Flamegraph: &querierv1.FlameGraph{
|
||||
|
@ -91,22 +91,30 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
logger.Error("Error GetProfile()", "err", err)
|
||||
return err
|
||||
}
|
||||
frame := responseToDataFrames(prof)
|
||||
|
||||
var frame *data.Frame
|
||||
if prof != nil {
|
||||
frame = responseToDataFrames(prof)
|
||||
|
||||
// If query called with streaming on then return a channel
|
||||
// to subscribe on a client-side and consume updates from a plugin.
|
||||
// Feel free to remove this if you don't need streaming for your datasource.
|
||||
if qm.WithStreaming {
|
||||
channel := live.Channel{
|
||||
Scope: live.ScopeDatasource,
|
||||
Namespace: pCtx.DataSourceInstanceSettings.UID,
|
||||
Path: "stream",
|
||||
}
|
||||
frame.SetMeta(&data.FrameMeta{Channel: channel.String()})
|
||||
}
|
||||
} else {
|
||||
// We still send empty data frame to give feedback that query really run, just didn't return any data.
|
||||
frame = getEmptyDataFrame()
|
||||
}
|
||||
responseMutex.Lock()
|
||||
response.Frames = append(response.Frames, frame)
|
||||
responseMutex.Unlock()
|
||||
|
||||
// If query called with streaming on then return a channel
|
||||
// to subscribe on a client-side and consume updates from a plugin.
|
||||
// Feel free to remove this if you don't need streaming for your datasource.
|
||||
if qm.WithStreaming {
|
||||
channel := live.Channel{
|
||||
Scope: live.ScopeDatasource,
|
||||
Namespace: pCtx.DataSourceInstanceSettings.UID,
|
||||
Path: "stream",
|
||||
}
|
||||
frame.SetMeta(&data.FrameMeta{Channel: channel.String()})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@ -273,6 +281,18 @@ func (pt *ProfileTree) String() string {
|
||||
return tree.String()
|
||||
}
|
||||
|
||||
func getEmptyDataFrame() *data.Frame {
|
||||
var emptyProfileDataFrame = data.NewFrame("response")
|
||||
emptyProfileDataFrame.Meta = &data.FrameMeta{PreferredVisualization: "flamegraph"}
|
||||
emptyProfileDataFrame.Fields = data.Fields{
|
||||
data.NewField("level", nil, []int64{}),
|
||||
data.NewField("value", nil, []int64{}),
|
||||
data.NewField("self", nil, []int64{}),
|
||||
data.NewField("label", nil, []string{}),
|
||||
}
|
||||
return emptyProfileDataFrame
|
||||
}
|
||||
|
||||
type CustomMeta struct {
|
||||
ProfileTypeID string
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user