Skip to content

Commit

Permalink
Fix differences in response structure
Browse files Browse the repository at this point in the history
This commit fixes a few issues regarding different response structures
with a `failures` property. E.g. the `_shards` response structure has a
`failures` property which returns different failures for the `reason`
property than the `failures[x].reason` property of `_nodes` (returned
from Cluster Stats API). I was confused due to this and messed up
7.0.25 because of it.

This hopefully fixes olivere#1494.
  • Loading branch information
olivere authored and dungnx-teko committed Sep 16, 2021
1 parent f7c88ff commit 05e6fc5
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 14 deletions.
21 changes: 14 additions & 7 deletions cluster_stats.go
Expand Up @@ -168,13 +168,20 @@ func (s *ClusterStatsService) Do(ctx context.Context) (*ClusterStatsResponse, er

// ClusterStatsResponse is the response of ClusterStatsService.Do.
type ClusterStatsResponse struct {
NodesStats *ShardsInfo `json:"_nodes,omitempty"`
Timestamp int64 `json:"timestamp"`
ClusterName string `json:"cluster_name"`
ClusterUUID string `json:"cluster_uuid"`
Status string `json:"status,omitempty"` // e.g. green
Indices *ClusterStatsIndices `json:"indices"`
Nodes *ClusterStatsNodes `json:"nodes"`
NodesStats *ClusterStatsNodesResponse `json:"_nodes,omitempty"`
Timestamp int64 `json:"timestamp"`
ClusterName string `json:"cluster_name"`
ClusterUUID string `json:"cluster_uuid"`
Status string `json:"status,omitempty"` // e.g. green
Indices *ClusterStatsIndices `json:"indices"`
Nodes *ClusterStatsNodes `json:"nodes"`
}

type ClusterStatsNodesResponse struct {
Total int `json:"total"`
Successful int `json:"successful"`
Failed int `json:"failed"`
Failures []*FailedNodeException `json:"failures,omitempty"`
}

type ClusterStatsIndices struct {
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Expand Up @@ -2,7 +2,7 @@ version: '3'

services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.13.2
image: docker.elastic.co/elasticsearch/elasticsearch:${VERSION:-7.13.2}
hostname: elasticsearch
environment:
- cluster.name=elasticsearch
Expand All @@ -29,7 +29,7 @@ services:
ports:
- 9200:9200
platinum:
image: docker.elastic.co/elasticsearch/elasticsearch:7.13.2
image: docker.elastic.co/elasticsearch/elasticsearch:${VERSION:-7.13.2}
hostname: elasticsearch-platinum
environment:
- cluster.name=platinum
Expand Down
24 changes: 19 additions & 5 deletions errors.go
Expand Up @@ -81,7 +81,9 @@ type ErrorDetails struct {
Grouped bool `json:"grouped,omitempty"`
CausedBy map[string]interface{} `json:"caused_by,omitempty"`
RootCause []*ErrorDetails `json:"root_cause,omitempty"`
Suppressed []*ErrorDetails `json:"suppressed,omitempty"`
FailedShards []map[string]interface{} `json:"failed_shards,omitempty"`
Header map[string]interface{} `json:"header,omitempty"`

// ScriptException adds the information in the following block.

Expand Down Expand Up @@ -204,11 +206,23 @@ func IsStatusCode(err interface{}, code int) bool {

// ShardsInfo represents information from a shard.
type ShardsInfo struct {
Total int `json:"total"`
Successful int `json:"successful"`
Failed int `json:"failed"`
Failures []*FailedNodeException `json:"failures,omitempty"`
Skipped int `json:"skipped,omitempty"`
Total int `json:"total"`
Successful int `json:"successful"`
Failed int `json:"failed"`
Failures []*ShardOperationFailedException `json:"failures,omitempty"`
Skipped int `json:"skipped,omitempty"`
}

type ShardOperationFailedException struct {
Shard int `json:"shard,omitempty"`
Index string `json:"index,omitempty"`
Status string `json:"status,omitempty"`
Reason map[string]interface{} `json:"reason,omitempty"`

// TODO(oe) Do we still have those?
Node string `json:"_node,omitempty"`
// TODO(oe) Do we still have those?
Primary bool `json:"primary,omitempty"`
}

// FailedNodeException returns an error on the node level.
Expand Down
48 changes: 48 additions & 0 deletions search_test.go
Expand Up @@ -1742,3 +1742,51 @@ func TestSearchWithDateMathIndices(t *testing.T) {
t.Errorf("expected len(SearchResult.Hits.Hits) = %d; got %d", want, got)
}
}

func TestSearchResultDecode(t *testing.T) {
tests := []struct {
Body string
}{
// #0 With _shards.failures
{
Body: `{
"took":1146,
"timed_out":false,
"_shards":{
"total":8,
"successful":6,
"skipped":0,
"failed":2,
"failures":[
{
"shard":1,
"index":"l9leakip-0000001",
"node":"AsQq1Dh2QxCSTRSLTg0vFw",
"reason":{
"type":"illegal_argument_exception",
"reason":"The length [1119437] of field [events.summary] in doc[2524900]/index[l9leakip-0000001] exceeds the [index.highlight.max_analyzed_offset] limit [1000000]. To avoid this error, set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them."
}
},
{
"shard":3,
"index":"l9leakip-0000001",
"node":"AsQq1Dh2QxCSTRSLTg0vFw",
"reason":{
"type":"illegal_argument_exception",
"reason":"The length [1023566] of field [events.summary] in doc[2168434]/index[l9leakip-0000001] exceeds the [index.highlight.max_analyzed_offset] limit [1000000]. To avoid this error, set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them."
}
}
]
},
"hits":{}
}`,
},
}

for i, tt := range tests {
var resp SearchResult
if err := json.Unmarshal([]byte(tt.Body), &resp); err != nil {
t.Fatalf("case #%d: expected no error, got %v", i, err)
}
}
}
9 changes: 9 additions & 0 deletions setup_test.go
Expand Up @@ -301,11 +301,20 @@ var (
logDeprecations = flag.String("deprecations", "off", "log or fail on deprecation warnings")
logTypesRemoval = flag.Bool("types-removal", false, "log deprecation warnings regarding types removal")
strict = flag.Bool("strict-decoder", false, "treat missing unknown fields in response as errors")
noSniff = flag.Bool("no-sniff", false, "allows to disable sniffing globally")
noHealthcheck = flag.Bool("no-healthcheck", false, "allows to disable healthchecks globally")
)

func setupTestClient(t logger, options ...ClientOptionFunc) (client *Client) {
var err error

if *noSniff {
options = append(options, SetSniff(false))
}
if *noHealthcheck {
options = append(options, SetHealthcheck(false))
}

client, err = NewClient(options...)
if err != nil {
t.Fatal(err)
Expand Down

0 comments on commit 05e6fc5

Please sign in to comment.