AWS SDK for Rubyでlocalstack + OpenSearch環境に接続する

ローカルのdocker環境に構築したlocalstack + OpenSearchAWS SDK for Rubyを用いた接続方法です。 接続方法自体はAWS公式のデベロッパーガイドに記載されていますが、クライアントクラス化して使用します。 docs.aws.amazon.com

docker-compose.yml

# rubyを実行するコンテナに環境変数を追加
environment:
  OPENSEARCH_ENDPOINT: http://localstack:4566/my-custom-endpoint
  AWS_ACCESS_KEY_ID: dummy
  AWS_SECRET_ACCESS_KEY: dummy

クライアントクラス

require 'aws-sdk-opensearchservice'

class OpenSearchClient
  def initialize
  end

  def request(http_method, api_path, document)
    uri = URI(endpoint + '/' + api_path)
    signature = generate_signature(http_method, api_path, document)
    http_client = Net::HTTP.new(uri.host, uri.port)
    http_client.use_ssl = Rails.env.production?
    http_client.start do |http|
      request = request_http_method(http_method, uri)
      request.body = document.to_json
      request['Host'] = signature.headers['host']
      request['X-Amz-Date'] = signature.headers['x-amz-date']
      request['X-Amz-Security-Token'] = signature.headers['x-amz-security-token']
      request['X-Amz-Content-Sha256']= signature.headers['x-amz-content-sha256']
      request['Authorization'] = signature.headers['authorization']
      request['Content-Type'] = 'application/json'
      http.request(request)
    end
  end

  private

  def endpoint
    @endpoint ||= ENV['OPENSEARCH_ENDPOINT']
  end

  def request_http_method(http_method, uri)
    case http_method
    when 'GET'
      Net::HTTP::Get.new(uri)
    when 'POST'
      Net::HTTP::Post.new(uri)
    when 'PUT'
      Net::HTTP::Put.new(uri)
    when 'DELETE'
      Net::HTTP::Delete.new(uri)
    end
  end

  def generate_signature(http_method, api_path, document)
    signer.sign_request(
      http_method: http_method,
      url: endpoint + '/' + api_path,
      body: document.to_json
    )
  end

  def signer
    @signer ||= Aws::Sigv4::Signer.new(
      service: 'es',
      region: 'ap-northeast-1',
      access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
    )
  end
end

インデックスの作成

document = { 
  settings: {
    index: {
      analysis: {
        tokenizer: {
          kuromoji_tokenizer: {
            type: 'kuromoji_tokenizer'
          }
        },
        analyzer: {
          analyzer: {
            type: 'custom',
            tokenizer: 'kuromoji_tokenizer'
          }
        }
      }
    }
  }
}

OpenSearchClient.new.request('PUT', 'sample-index', document)
=> "{\"acknowledged\":true,\"shards_acknowledged\":true,\"index\":\"sample-index\"}"