errors.go 5.0 KB

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