表題通りなんですが。ちょっとハマりました。
elasticsearch-api 本体側が修正されました
Karelさんが教えてくれた! Thank you karel!
type パラメータを bulk api の呼び出し側で削除するような対応が行われたようです。これで不格好な workaround を抹消できて、俺ハッピー。
transport_options で Content-Type 指定することで回避できました
@xend 様にコメントで教えていただきました。Clientのコンストラクタで transport_options を指定することで対応できるようです。うーむ。jsonで送信するのであれば application/x-www-form-urlencoded でなぜ送る…。(2015/10/7追記)
以下ハマったのと workaround の経緯
$ curl mysearch.ap-southeast-1.es.amazonaws.com/_search/? -X POST -d '{}'
HTTP/1.1 403 Forbidden
Content-Type: application/json
Server: Jetty(8.1.12.v20130726)
x-amzn-RequestId: **********************
Content-Length: 135
Connection: keep-alive
{"message":"When Content-Type:application/x-www-form-urlencoded, URL cannot include query-string parameters (after '?'): '/_search/?'"}
ContentType が x-www-form-urlencoded だった場合、query-string が含まれたURLだと403でエラーになります (>_<!!
マジで?
これが問題になったのは、elasticsearch-railsを使ってバルクインサートしてる箇所であり。
調べて見ると、elasticsearch-api の bulk アクション(これ)が type パラメータを query-string として渡しているのが原因であろうと思われた。というかそれが上の制約と衝突してるのは間違いない。
ということで取り急ぎ type だけを特別扱いするように残念すぎるモンキーパッチを用意。
[ec2-user:~/my_rails_app]$ cat config/initializers/elasticsearch_patch.rb
# type を valid_params から除外する
module Elasticsearch
module API
module Actions
def bulk(arguments={})
valid_params = [
:consistency,
:refresh,
:replication,
# :type,
:timeout ]
method = HTTP_POST
path = Utils.__pathify Utils.__escape(arguments[:index]), Utils.__escape(arguments[:type]), '_bulk'
params = Utils.__validate_and_extract_params arguments, valid_params
body = arguments[:body]
if body.is_a? Array
payload = Utils.__bulkify(body)
else
payload = body
end
perform_request(method, path, params, payload).body
end
end
end
end
最初 elasticsearch-rails のバージョンが 1.7系だからじゃないかとか、Amazon ESのElasticsearchが1.5.2だからなんじゃねーかとかいろいろ疑ったのだが、空振りでした。
http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html こことか参照すると、Parameters in HTTP POST requests が、**The service ignores parameters passed in URLs for HTTP POST requests signed with Signature Version 4.**とか書かれているので突破する方法はあるのかもしれない。が、手作業で署名したアクセスを作ってみても Response body: {"message":"Credential should be scoped to correct service: 'es'. "}
とか言われてしまうので手詰まり感がある。Admin権限持ってるのにー。
かくして、上のようなパッチを置くことになってしまった。本当に反省している。しかし elasticsearch-api の方の不適切な挙動って感じでもないので、難しいところです。このやりかただと refresh とか consistency とか timeout とか指定できないし。うーん。