From 05e6fc59b39ae32d0b1873b8c4a2844c9d29ad1e Mon Sep 17 00:00:00 2001 From: Oliver Eilhard Date: Wed, 7 Jul 2021 17:13:07 +0200 Subject: [PATCH] Fix differences in response structure 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 #1494. --- cluster_stats.go | 21 +++++++++++++------- docker-compose.yml | 4 ++-- errors.go | 24 ++++++++++++++++++----- search_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++ setup_test.go | 9 +++++++++ 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/cluster_stats.go b/cluster_stats.go index 711c2a76d..7bb39cf65 100644 --- a/cluster_stats.go +++ b/cluster_stats.go @@ -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 { diff --git a/docker-compose.yml b/docker-compose.yml index 4b8761cff..a3054a66b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 @@ -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 diff --git a/errors.go b/errors.go index c17c5ed54..f3bae9d09 100644 --- a/errors.go +++ b/errors.go @@ -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. @@ -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. diff --git a/search_test.go b/search_test.go index 8129e7f3e..1513551c9 100644 --- a/search_test.go +++ b/search_test.go @@ -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) + } + } +} diff --git a/setup_test.go b/setup_test.go index bf8e03b58..38964b89d 100644 --- a/setup_test.go +++ b/setup_test.go @@ -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)