errors.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Copyright 2012-present Oliver Eilhard. All rights reserved.
  2. // Use of this source code is governed by a MIT-license.
  3. // See http://olivere.mit-license.org/license.txt for details.
  4. package elastic
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "io/ioutil"
  9. "net/http"
  10. "github.com/pkg/errors"
  11. )
  12. // checkResponse will return an error if the request/response indicates
  13. // an error returned from Elasticsearch.
  14. //
  15. // HTTP status codes between in the range [200..299] are considered successful.
  16. // All other errors are considered errors except they are specified in
  17. // ignoreErrors. This is necessary because for some services, HTTP status 404
  18. // is a valid response from Elasticsearch (e.g. the Exists service).
  19. //
  20. // The func tries to parse error details as returned from Elasticsearch
  21. // and encapsulates them in type elastic.Error.
  22. func checkResponse(req *http.Request, res *http.Response, ignoreErrors ...int) error {
  23. // 200-299 are valid status codes
  24. if res.StatusCode >= 200 && res.StatusCode <= 299 {
  25. return nil
  26. }
  27. // Ignore certain errors?
  28. for _, code := range ignoreErrors {
  29. if code == res.StatusCode {
  30. return nil
  31. }
  32. }
  33. return createResponseError(res)
  34. }
  35. // createResponseError creates an Error structure from the HTTP response,
  36. // its status code and the error information sent by Elasticsearch.
  37. func createResponseError(res *http.Response) error {
  38. if res.Body == nil {
  39. return &Error{Status: res.StatusCode}
  40. }
  41. data, err := ioutil.ReadAll(res.Body)
  42. if err != nil {
  43. return &Error{Status: res.StatusCode}
  44. }
  45. errReply := new(Error)
  46. err = json.Unmarshal(data, errReply)
  47. if err != nil {
  48. return &Error{Status: res.StatusCode}
  49. }
  50. if errReply != nil {
  51. if errReply.Status == 0 {
  52. errReply.Status = res.StatusCode
  53. }
  54. return errReply
  55. }
  56. return &Error{Status: res.StatusCode}
  57. }
  58. // Error encapsulates error details as returned from Elasticsearch.
  59. type Error struct {
  60. Status int `json:"status"`
  61. Details *ErrorDetails `json:"error,omitempty"`
  62. }
  63. // ErrorDetails encapsulate error details from Elasticsearch.
  64. // It is used in e.g. elastic.Error and elastic.BulkResponseItem.
  65. type ErrorDetails struct {
  66. Type string `json:"type"`
  67. Reason string `json:"reason"`
  68. ResourceType string `json:"resource.type,omitempty"`
  69. ResourceId string `json:"resource.id,omitempty"`
  70. Index string `json:"index,omitempty"`
  71. Phase string `json:"phase,omitempty"`
  72. Grouped bool `json:"grouped,omitempty"`
  73. CausedBy map[string]interface{} `json:"caused_by,omitempty"`
  74. RootCause []*ErrorDetails `json:"root_cause,omitempty"`
  75. FailedShards []map[string]interface{} `json:"failed_shards,omitempty"`
  76. }
  77. // Error returns a string representation of the error.
  78. func (e *Error) Error() string {
  79. if e.Details != nil && e.Details.Reason != "" {
  80. return fmt.Sprintf("elastic: Error %d (%s): %s [type=%s]", e.Status, http.StatusText(e.Status), e.Details.Reason, e.Details.Type)
  81. } else {
  82. return fmt.Sprintf("elastic: Error %d (%s)", e.Status, http.StatusText(e.Status))
  83. }
  84. }
  85. // IsConnErr returns true if the error indicates that Elastic could not
  86. // find an Elasticsearch host to connect to.
  87. func IsConnErr(err error) bool {
  88. return err == ErrNoClient || errors.Cause(err) == ErrNoClient
  89. }
  90. // IsNotFound returns true if the given error indicates that Elasticsearch
  91. // returned HTTP status 404. The err parameter can be of type *elastic.Error,
  92. // elastic.Error, *http.Response or int (indicating the HTTP status code).
  93. func IsNotFound(err interface{}) bool {
  94. return IsStatusCode(err, http.StatusNotFound)
  95. }
  96. // IsTimeout returns true if the given error indicates that Elasticsearch
  97. // returned HTTP status 408. The err parameter can be of type *elastic.Error,
  98. // elastic.Error, *http.Response or int (indicating the HTTP status code).
  99. func IsTimeout(err interface{}) bool {
  100. return IsStatusCode(err, http.StatusRequestTimeout)
  101. }
  102. // IsConflict returns true if the given error indicates that the Elasticsearch
  103. // operation resulted in a version conflict. This can occur in operations like
  104. // `update` or `index` with `op_type=create`. The err parameter can be of
  105. // type *elastic.Error, elastic.Error, *http.Response or int (indicating the
  106. // HTTP status code).
  107. func IsConflict(err interface{}) bool {
  108. return IsStatusCode(err, http.StatusConflict)
  109. }
  110. // IsStatusCode returns true if the given error indicates that the Elasticsearch
  111. // operation returned the specified HTTP status code. The err parameter can be of
  112. // type *http.Response, *Error, Error, or int (indicating the HTTP status code).
  113. func IsStatusCode(err interface{}, code int) bool {
  114. switch e := err.(type) {
  115. case *http.Response:
  116. return e.StatusCode == code
  117. case *Error:
  118. return e.Status == code
  119. case Error:
  120. return e.Status == code
  121. case int:
  122. return e == code
  123. }
  124. return false
  125. }
  126. // -- General errors --
  127. // shardsInfo represents information from a shard.
  128. type shardsInfo struct {
  129. Total int `json:"total"`
  130. Successful int `json:"successful"`
  131. Failed int `json:"failed"`
  132. }
  133. // shardOperationFailure represents a shard failure.
  134. type shardOperationFailure struct {
  135. Shard int `json:"shard"`
  136. Index string `json:"index"`
  137. Status string `json:"status"`
  138. // "reason"
  139. }