この手の管理プログラムは、いちいちJavaを立ち上げるのも気が重いので、Rubyのright_awsを利用してボチボチ作り始めました。right_awsのバージョンは2.1.0です。
require 'rubygems' require 'right_aws' AWS_ACCESS_KEY = 'アクセスキー' AWS_SECRET_KEY = 'シークレットキー' client = RightAws::Ec2.new(AWS_ACCESS_KEY, AWS_SECRET_KEY, :server => 'ap-northeast-1.ec2.amazonaws.com') client.create_tags('vol-12345678', {'Name' => '日本語!'})
で、実行するとSignatureが合わないというエラーでコケます。
/Library/Ruby/Gems/1.8/gems/right_aws-2.1.0/lib/awsbase/right_awsbase.rb:545:in `request_info_impl': SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. (RightAws::AwsError) from /Library/Ruby/Gems/1.8/gems/right_aws-2.1.0/lib/ec2/right_ec2.rb:140:in `request_info' from /Library/Ruby/Gems/1.8/gems/right_aws-2.1.0/lib/ec2/right_ec2_tags.rb:79:in `create_tags' .....ところが、{'Name' => 'HogeHoge!!'}のようにシングルバイトの文字列を与えた場合はうまく行きます。
おっかしい…マルチバイト関連の問題だろう…めんどくさー、とか思いつつ、自分を奮い立たせる為にツイートしていたら(笑)なんと@junyaさんも同様の現象にあったとの事で、サポートフォーラムにも投稿したとの事。
Java SDKではうまく行くとの事で、これはRubyそのものの問題か、もしくはright_aws側の問題だろうなーと思いつつ、Java SDKで同じ事をやったら確かに問題なく行きます。
package test1; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.CreateTagsRequest; import com.amazonaws.services.ec2.model.Tag; public class CreateTagsTest { public static void main(String[] args) { final String AWS_ACCESS_KEY = "アクセスキー"; final String AWS_SECRET_KEY = "シークレットキー"; AWSCredentials cred = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY); AmazonEC2Client client = new AmazonEC2Client(cred); client.setEndpoint("https://ec2.ap-northeast-1.amazonaws.com"); client.createTags(new CreateTagsRequest().withResources("vol-12345678") .withTags(new Tag().withKey("Name").withValue("日本語!"))); } }
仕方が無いので、Java SDKとright_awsの両方のソースを掘ってみたところ、やはりSignatureの生成のところで違いがありました。
Java側のクラスはcom.amazonaws.auth.QueryStringSignerです。下記、一部抜粋。
private String calculateStringToSignV2(URI endpoint, Map「POST」という文字がハードコーディングされていますねwparameters) throws AmazonClientException { StringBuilder data = new StringBuilder(); data.append("POST").append("\n"); data.append(getCanonicalizedEndpoint(endpoint)).append("\n"); data.append(getCanonicalizedResourcePath(endpoint)).append("\n"); data.append(getCanonicalizedQueryString(parameters)); return data.toString(); }
では、Ruby側はどうなっているかというと、create_tagsメソッドがright_ec2_tags.rbに定義されています。※構造がJavaと異なるので単純比較が出来ないのです。
module RightAws class Ec2 (中略) def create_tags(resources, tags, options={}) default = options[:default].nil? ? '' : options[:default] params = amazonize_list("ResourceId", resources) params.merge! amazonize_list(['Tag.?.Key', 'Tag.?.Value'], tags, :default => default) link = generate_request("CreateTags", params) request_info(link, RightBoolResponseParser.new(:logger => @logger)) rescue Exception on_exception end (中略) end end
問題は、このメソッドの中でgenerate_request("CreateTags", params)しているところです。これはright_ec2.rbに定義されおりまして、びっくりする事が書いてありました。
module RightAws class Ec2 < RightAwsBase include RightAwsBaseInterface (中略) def generate_request(action, params={}) #:nodoc: generate_request_impl(:get, action, params ) end (中略) end endget決め打ちキタコレ! というわけで、うまく動作するJavaに習って、right_aws側をこのようにしてみました。
require 'rubygems' require 'right_aws' AWS_ACCESS_KEY = 'アクセスキー' AWS_SECRET_KEY = 'シークレットキー' client = RightAws::Ec2.new(AWS_ACCESS_KEY, AWS_SECRET_KEY, :server => 'ap-northeast-1.ec2.amazonaws.com') def client.generate_request(action, params) verb = :get if action.start_with? 'Create' then verb = :post end generate_request_impl(verb, action, params) end client.create_tags('vol-12345678', {'Name' => '日本語!'})
generate_requestの挙動をCreateなんちゃらの時にPOSTに置き換えるという付け焼き刃対応ですw
結果的にこれでうまく行きました。
シングルバイトの時はgetでうまく行くのがどうにも納得いかないのですが…一応、ご報告です。
追記
Ruby 1.8.7で試した結果です。Ruby 1.9.2ではうまく行かないとの報告がありました。