Skip to content

Commit 7463266

Browse files
authored
Query: always return a string in the lastError field (#2809)
* Query: always return a string in the `lastError` field While testing out the new React UI, I have found out that some errors return empty dicts when marshalled into the JSON format. The response looks like this: ``` {..., "lastError": {}} ``` And then, obviously, the UI fails to parse that. Add a `stringifiedError` type with tests which ensures that we always get a string. Signed-off-by: Giedrius Statkevičius <giedriuswork@gmail.com> * CHANGELOG: add PR number + remove whitespace Signed-off-by: Giedrius Statkevičius <giedriuswork@gmail.com> * query/storeset: fix a subtle bug Signed-off-by: Giedrius Statkevičius <giedriuswork@gmail.com> * Make changes according to Bartek's suggestions Signed-off-by: Giedrius Statkevičius <giedriuswork@gmail.com>
1 parent a61789d commit 7463266

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ We use *breaking* word for marking changes that are not backward compatible (rel
1919
- [#2728](https://github.com/thanos-io/thanos/pull/2728) Query: Fixed panics when using larger number of replica labels with short series label sets.
2020
- [#2787](https://github.com/thanos-io/thanos/pull/2787) Update Prometheus mod to pull in prometheus/prometheus#7414.
2121
- [#2807](https://github.com/thanos-io/thanos/pull/2807) Store: decreased memory allocations while querying block's index.
22+
- [#2809](https://github.com/thanos-io/thanos/pull/2809) Query: `/api/v1/stores` now guarantees to return a string in the `lastError` field.
2223

2324
### Changed
2425

pkg/query/storeset.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package query
55

66
import (
77
"context"
8+
"encoding/json"
89
"fmt"
910
"sort"
1011
"strings"
@@ -47,10 +48,26 @@ type RuleSpec interface {
4748
Addr() string
4849
}
4950

51+
// stringError forces the error to be a string
52+
// when marshalled into a JSON.
53+
type stringError struct {
54+
originalErr error
55+
}
56+
57+
// MarshalJSON marshals the error into a string form.
58+
func (e *stringError) MarshalJSON() ([]byte, error) {
59+
return json.Marshal(e.originalErr.Error())
60+
}
61+
62+
// Error returns the original underlying error.
63+
func (e *stringError) Error() string {
64+
return e.originalErr.Error()
65+
}
66+
5067
type StoreStatus struct {
5168
Name string `json:"name"`
5269
LastCheck time.Time `json:"lastCheck"`
53-
LastError error `json:"lastError"`
70+
LastError *stringError `json:"lastError"`
5471
LabelSets []storepb.LabelSet `json:"labelSets"`
5572
StoreType component.StoreAPI `json:"-"`
5673
MinTime int64 `json:"minTime"`
@@ -510,7 +527,7 @@ func (s *StoreSet) updateStoreStatus(store *storeRef, err error) {
510527
status = *prev
511528
}
512529

513-
status.LastError = err
530+
status.LastError = &stringError{originalErr: err}
514531

515532
if err == nil {
516533
status.LastCheck = time.Now()

pkg/query/storeset_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ package query
55

66
import (
77
"context"
8+
"encoding/json"
89
"fmt"
910
"math"
1011
"net"
1112
"testing"
1213
"time"
1314

1415
"github.com/fortytw2/leaktest"
16+
"github.com/pkg/errors"
1517
"github.com/thanos-io/thanos/pkg/component"
1618
"github.com/thanos-io/thanos/pkg/store"
1719
"github.com/thanos-io/thanos/pkg/store/storepb"
@@ -660,7 +662,7 @@ func TestQuerierStrict(t *testing.T) {
660662
testutil.Equals(t, 2, len(storeSet.stores), "two static clients must remain available")
661663
testutil.Equals(t, curMin, storeSet.stores[staticStoreAddr].minTime, "minimum time reported by the store node is different")
662664
testutil.Equals(t, curMax, storeSet.stores[staticStoreAddr].maxTime, "minimum time reported by the store node is different")
663-
testutil.NotOk(t, storeSet.storeStatuses[staticStoreAddr].LastError)
665+
testutil.NotOk(t, storeSet.storeStatuses[staticStoreAddr].LastError.originalErr)
664666
}
665667

666668
func TestStoreSet_Update_Rules(t *testing.T) {
@@ -778,3 +780,28 @@ func TestStoreSet_Update_Rules(t *testing.T) {
778780
})
779781
}
780782
}
783+
784+
type weirdError struct {
785+
originalErr error
786+
}
787+
788+
// MarshalJSON marshals the error and returns weird results.
789+
func (e *weirdError) MarshalJSON() ([]byte, error) {
790+
return json.Marshal(map[string]string{})
791+
}
792+
793+
// Error returns the original, underlying string.
794+
func (e *weirdError) Error() string {
795+
return e.originalErr.Error()
796+
}
797+
798+
func TestStringError(t *testing.T) {
799+
weirdError := &weirdError{originalErr: errors.New("test")}
800+
properErr := &stringError{originalErr: weirdError}
801+
storestatusMock := map[string]error{}
802+
storestatusMock["weird"] = weirdError
803+
storestatusMock["proper"] = properErr
804+
b, err := json.Marshal(storestatusMock)
805+
testutil.Ok(t, err)
806+
testutil.Equals(t, []byte(`{"proper":"test","weird":{}}`), b, "expected to get proper results")
807+
}

0 commit comments

Comments
 (0)