ストレージを伴うテストのためにminioを使うようにしようとしたのですが、設定をしただけではうまく動きませんでした。
timecopを使っているところで、aws-sdkがエラーを起こしました。timecopは時間を固定したり過去・未来に移動したりするライブラリです。
https://github.com/travisjeffery/timecop
エラーメッセージは以下のようなものでした。
Aws::S3::Errors::RequestTimeTooSkewed: The difference between the request time and the server's time is too large.
このメッセージでググるとわかるのが、アプリケーションサーバとS3のサーバ(minio)の間で時刻が離れすぎているから起きる例外でした。timecopで3年前とかに移動しているため、当然起きました…😩
解決方法
解決方法は、aws-sdk-s3から時刻の確認をするときだけ、Time.now
が現在の時刻を返せばいいというアプローチでした。
参考にしたのは以下のページです。
このページの、例外に備えよう、という見出しのところに書いてあります。
まんまでは動かない
しかし、記事自体が3年前のものなためか、aws-sdkのバージョンが変わり、まんまでは動かなくなっていました。
# aws-sdk-s3では動かないので注意 class Time class << self def now_wrap if (caller || []).first.match('aws-sdk') now_without_mock_time # return real time else mock_time || now_without_mock_time # return mock time or real time end end alias_method :now, :now_wrap end end
修正
置き換えなければならない箇所の呼び出し元のパス情報が、aws-sdkではなく、aws-sigを含むものに変わっていたので、修正しました。
class Time class << self def now_wrap # p caller.first if caller.first.match('aws') # awsという文字列でなんとなくあたりを付けた # すると、aws-sigというものを発見 if (caller || []).first.match('aws-sig') # aws-sdkをaws-sigに変更 now_without_mock_time # return real time else mock_time || now_without_mock_time # return mock time or real time end end alias_method :now, :now_wrap end end
修正後、テストを実行したところ、timecopを使っている箇所でもテストが成功しました👍
他に試したこと
最初は、エラーメッセージから、アプリケーションサーバとS3サーバの時刻の比較をしないで済むオプションがあるのではないか?と考え、S3クライアントにそれっぽいものを設定してみたのですが、結局ダメでした。
s3_client = Aws::S3::Client.new( access_key_id: Rails.application.credentials.aws_access_key_id, secret_access_key: Rails.application.credentials.aws_secret_access_key, region: Rails.application.credentials.aws_region, correct_clock_skew: false, # default true retry_mode: 'standard', # default 'legacy' )