Rubyのややこしい配列とハッシュとシンボルについて整理してみた
皆様、あけましておめでとうございます。 さて、2015年の初投稿は、私的に今最も熱いRubyネタで始めようかと思います。 まず基礎のおさらいとして、Rubyにおける配列には一次元配列のarrayクラスオブジェクトと多次元配列(連想配列)であるhashクラスオブジェクトの二種類があります。それぞれの配列オブジェクトは定義する際に要素を囲い込むリテラル記号によって以下のように区別されています。 array = ["A", "B", "C"] # 配列変数arrayを定義 hash1 = {:first => "A", :second => "B", :third => "C"} # ハッシュhash1を定義 一次元配列であるarrayクラスオブジェクトは各要素値に数値添字が自動で割り振られるため、数値添字のインデックスにて各要素にアクセスができます。 array = ["A", "B", "C"] puts array[0] # "A"が表示される 一方hashクラスオブジェクトはキー・バリュー型のオブジェクトなので、文字列型の添字(キー)で要素値にアクセスができます。 hash2 = {"first" => "A", "second" => "B", "third" => "C"} puts hash2["first"] # "A"が表示される さて、ここで一番最初の例で定義したhash1とhash2では、キーの指定の仕方が異なっていることに気づきましたでしょうか? hash1とhash2は要素値こそ一緒ですが、キーが異なるため同じハッシュではありません。...AWS SDK for Rubyを使ってEC2インスタンスのステータスを確認する
アプリケーション側で、任意のAWSのEC2インスタンスについて、現在の稼働状況をリアルタイムに確認したい時がある。例えば外部アプリケーションからEC2インスタンスを起動させたり、停止させたりする場合などに、インスタンスの稼動状況を確認してステータスが変わったら次のプロセスを実行したいとかいうケースが、それに当てはまる。 SDK for Rubyでは、AWS::EC2クラスのclient.describe_instance_statusメソッドで特定のECインスタンスのステータスを取得することができる。インスタンスのステータスにはsystem_statusとinstance_statusの2種類があって、正確な稼動状態を確認したい場合はこの2つのステータスを取得する必要がある。 そして、注意しないといけないのが、インスタンスが停止している時はステータスが存在していないということだ。つまり、停止しているインスタンスに対してclient.describe_instance_statusメソッドを実行した場合、ステータス情報が格納されているinstance_status_setオブジェクトの中身が取得できないので、それを踏まえてコーディングしないと、ステータス取得エラーとなってしまう。 例えば、停止状態のインスタンスを起動した場合のステータスを監視する時など、最初はインスタンスが停止しているので、instance_status_setオブジェクト内にステータスがないということをあらかじめ想定して処理を作る必要がある。 また、ターミネートされたインスタンスはclient.describe_instance_statusメソッドを実行する対象自体がないことので、こちらも注意する必要がある。 さて、上記もろもろを踏まえてEC2インスタンスのステータス確認するRubyプログラムを作ってみた。 chkins.rb # encoding: utf-8 require "aws-sdk" def check_instance_status ec2 = AWS::EC2.new( :access_key_id => Params[:access_key_id], :secret_access_key => Params[:secret_access_key], :region => Params[:region] ) AWS.memoize do status = [] if ec2.instances[Params[:instance_id]].exists? ec2info = ec2.client.describe_instance_status({ 'instance_ids' => [Params[:instance_id]] }) if ec2info.instance_status_set.empty? # instance has stopped status << 'stopped' message = '%s has stopped' else ec2info....Mac どこでもターミナル
最近ubuntuからmacに乗り換えました。 これでiPhoneアプリ開発し放題だぜ!(ウソ しかし同じunix由来のOSとはいえ、異なる点もちらほら。 一番気になるのが、基本いちアプリ、いちウィンドウである点。 たくさん仮想デスクトップを生成してあっちこっちでターミナルを立ち上げたい派の私としては、この仕様がイマイチなじまないのです。 まあ仮想デスクトップ使いまくり、ターミナル立ち上げまくりなのはubuntuerの悪いクセだとは思いますが。 複数のターミナル立ち上げようと思ったら 1.spotlightでterminal.appを呼び出す。 2.⌘ + N で新しいターミナルを表示 3.表示されたターミナルを目的の仮想デスクトップまでドラック だるぃ…. ggってみると、スクリプトをアプリケーション化する方法があるみたいなので作ってみました。 https://blog.colorkrew.com/uploads/T.zip zipを展開してApplicationsに放り込めば使用可能です。 ▪️使用方法 1. spotlightでT.appと入力(t.で補完されるはず) 2. 今いる仮想デスクトップでターミナル起動! やってることは open -n /Applications/Utilities/Terminal.app だけです。 では良いターミナルライフを! ▪️参考 技術/MacOSX/シェルスクリプトを".app"(“Bundle”)化する http://www.glamenv-septzen.net/view/1201...MySQL5.5以降 sjis環境でDATETIMEにindexが効かず遅い
MySQLを5.6にバージョンアップした際に、一部のクエリがやたら遅くなってしまい困りました。 散々ggった結果、ヒットした情報が以下。 character_set_connection = sjis でDATETIME型のカラムにインデックスがきかないはなし http://bugs.mysql.com/bug.php?id=74449 なんと、character_set_connection = sjis の場合に限ってDATETIME型,DATE型カラムに誤った型変換が行われるらしい。 sjisなんてやめなはれ、は正論ですが、残念ながらsjisを捨てられるシステムではないので、以下のようにCASTで対処。 date型の場合 AND dl.date >= '2014-08-10' ↓ AND dl.date >= cast('2014-08-10' as date) datetime型の場合 AND dl.date >= '2014-08-10' ↓ AND dl.date >= cast('2014-08-10' as datetime) ご参考まで。...Capistrano3のタスク内でトップレベルのタスク名を取得する
Capistrano3でタスク書いていて、タスク内でcapコマンドとして実行されたトップレベルのタスク名を引いて、処理を分岐させる必要が生じて、そのトップレベルのタスク名取得に苦労したので、ここに備忘録化しておく。 基本的にCapistrano3でタスク内で自分のタスク名を取得したい場合、次のようにブロックの引数として引ける。 desc "main task" task :main_task => :sub_task do |task| run_locally do info ": This task name: #{task}" info ": Running main deploy task!" end end desc "sub task" task :sub_task do |task| run_locally do current_task = task.name_with_args.split(':').last info ": This task name: #{current_task}" end end 上記タスクを実行してみると、こうなる。 $ cap test deploy:main_task INFO: This task name: sub_task INFO: This task name: deploy:main_task INFO: Running main deploy task!...素のRubyでMySQLクエリの結果を取得する
Ruby+MySQLの処理をする時、たいていはMySQL操作系のライブラリでruby-mysqlやmysql2とかを使うケースが多いのだろうが、それらのライブラリなしの素のRuby環境でMySQLのクエリ操作を行う必要があったので、その際に書いたソースを汎用化してまとめてみた。まぁ…ほとんどニーズはないかもしれんが、一応TIPSとして共有化しておこうかと。 mysql_query.rb db = { :db_config => "~/.user.cnf", :db_name => "database_name" } query = "SELECT * FROM db_table_name WHERE primary_key_id = 1;" res = `/usr/bin/mysql --defaults-extra-file=#{db.fetch(:db_config)} -D #{db.fetch(:db_name)} -e \"#{query}\" ` if res.empty? then p "data is none." else lines = res.split("\n") columns = lines.first.split("\t") results = [] lines.each_with_index { |line, i| if i > 0 then tmp_hash = {} values = line....authorクエリを利用したWordPressのユーザー名漏洩を防ぐ方法
DEVLABはWordPressで運用しているのだが、そのWordPressについて気になる記事を見つけた。 WordPressサイトのホームURLに「?author=x」のGETクエリをつけることで、サイト内のユーザー名がばれてしまう というものだ(※ 詳しくはMT SystemsさんのWordPress TIPを参照)。?author=xのxは数値で、WordPressのユーザーID(ユーザーテーブルのプライマリキー)となる。つまりxに1を指定すると、WordPressのルート管理ユーザーになるわけだ。 さっそく、DEVLABでも試してみたところ・・・ http://devlab.isao.co.jp?author=1がみごとにパーマリンク設定で指定されているリライトルールに沿ってリライトされ、https://blog.colorkrew.com/author/<ルート管理ユーザー名>/にリダイレクトされてしまった。このままだと、管理ユーザーのユーザー名に対してパスワードの総当りをされて、もしログイン成功とかされちゃうと、サイトがクラッキングされてしまうじゃないですか! ただ、DEVLABの場合、一般的なWordPressのログイン画面wp-login.phpは無効化してあって、ログイン方法を探すのが大変なので、一応は安全ではある。しかし、ユーザー名が漏洩してしまう脆弱性があるのは防止しないといけないので、対策を考えてみた次第。 MT Systemsさんのサイトで紹介されているように、著作者アーカイブページのテンプレートauthor.phpによってリダイレクトしてしまう方法ではHTTPのレスポンスヘッダにリダイレクトのlocationとしてユーザー名が出力されてしまうため、たとえ.htaccessにrewriteルールを追加したとしても防止できないのが厄介だ。 MT Systemsさんのサイトでは、最終的にユーザーデータのuser_nicenameを書き換えて、ログインアカウントであるuser_loginと異なる値にしてしまう対処策が掲載されていたんだが、管理パネルから直接編集できない項目でもあって、なかなかに難儀である。 もうちょっと簡単に対策できないものか…。 ──と、言うわけで、対策方法を自作してみた。 // prevent the leakage of user name by author query on WordPress. function knockout_author_query() { // disable author rewrite rule global $wp_rewrite; $wp_rewrite->flush_rules(); $wp_rewrite->author_base = ''; $wp_rewrite->author_structure = '/'; // for author query request if (isset($_REQUEST['author']) && !...Capistrano3でタスクが二重起動してしまう時の対処法
Capistrano3でステージ環境を変えてタスクを実行した時に、タスクが二重起動するという症状に陥った。二重でタスクが実行されるので、ラウンチするAWSインスタンスを“2つ”と設定していても“4つ”起つし、MySQLに発行するクエリも2倍になって、INSERTするレコードが重複して挿入される。一時的にファイルに書き出していた設定なども二重起動している後続タスクによって上書きされてしまい、もうデプロイはしっちゃかめっちゃかな状態だった。 解決できたので、その時の対処法を備忘録として残しておく。 原因はinvoke設定でデフォルトのデプロイ環境を指定していたためだった。 はじめ、試験環境でデプロイタスクの開発を行っていた時、Capfileに下記のような設定を書いていた。 Rake::Task[:develop].invoke invoke :develop これを書いておくと、デフォルトデプロイ環境がdevelopに固定化されるので、デプロイコマンドを実行するときに本来ならデプロイ環境(ステージ名)を指定して、 $ cap develop deploy:task_name のようにするところを、 $ cap deploy:task_name と、デプロイ環境を省略できてちょっとだけ楽だったのだが、これがデフォルトデプロイ環境以外にデプロイを行う時に悪影響を及ぼしたのだ。 今回、試験環境でデプロイが上手くいったので、次に本番環境でデプロイを行うことになり、デプロイ環境(ステージ名)をproductionで実行することになった。デプロイ内容は試験環境とまったく同一だったため、デプロイタスク設定の差分はなく、ロール設定やSSH接続設定も同じだったため、config/deploy/develop.rbをコピーしてconfig/deploy/production.rbを作成していた。 この状態で、下記のようにデプロイ環境を指定してデプロイを実行した次第。 $ cap production deploy:task_name これだと、省略されているデフォルトデプロイ環境へのデプロイも同時に起動してしまうわけです。実際には、 $ cap develop production deploy:task_name と環境を二つ指定してデプロイを実行しているのと同じことになっているわけです。 この状態を解消するには、前述のinvoke設定を削除するだけです。 というか、複数環境に対してデプロイが発生する時は混乱の元になるんで、invoke設定とかはしない方がいいですね。 いやはや、何気に解決するまで数時間ハマってました。今後は注意しないと。...Capistrano3でEC2インスタンス新規作成から初期設定までのデプロイ(まとめ)
ここまでAWSのEC2インスタンスを新規作成して、そのインスタンスに対しての初期設定までを、Capistrano3でタスク化することをやって来ました。難儀したものの、ようやくEC2インスタンスの準備が出来て、あとはミドルウェアやアプリケーションをインストールするだけ──というところまでたどり着きました。そこで今回は、これまでのデプロイの流れを一度総括してまとめてみようかと思います。 まず、Capistrano3を稼動させるデプロイサーバの準備から。公式のAmazon Linux環境などのEC2インスタンスをAWSマネージメントコンソール等からラウンチして、ログインしたら、Capistrano3をインストールします1。 # yum update -y # yum groupinstall -y "Development Tools" # openssl version OpenSSL 1.0.1h-fips 5 Jun 2014 # openssl version -d OPENSSLDIR: "/etc/pki/tls" # curl -L https://get.rvm.io | bash -s stable # source /etc/profile.d/rvm.sh # rvm list (※ rvm rubies と表示されればインストール完了) # ruby -v ruby 2.0.0p451 (2014-02-24 revision 45167) [x86_64-linux] # rvm install 2....Capistranoのタスクを新EC2インスタンスが完全起動するまでsleepさせる
Capistranoで新規作成したEC2インスタンスが完全に起動し切っていない状態で、そのインスタンスに対してSSHアクセスするタスクを実行すると、どこかしらでエラーになってタスクが完了しません。そこで、デプロイ対象となるEC2インスタンスの起動状態をチェックして、完全に起動していない状態の場合sleepして起動を待つようなタスクを作りました。 AWSのEC2インスタンスには3つのステータス情報があり、この全てのステータスを確認しないと、インスタンスの完全起動状態とは言えないので、注意が必要でした(下図参照)。 インスタンスが起動しているかどうかの確認は、AWSマネージメントコンソールでいうところの「Instance State」がrunningであるかを判定すればOKなのですが、実際にインスタンスにSSH接続ができるかどうかの確認は「Status Checks」欄に「2/2 checks」とあるように2つのステータス(INSTANCESTATUSとSYSTEMSTATUSのreachability)が共にpassedであるかまでを確認する必要があるのです。通常、インスタンスが起動すると「Instance State」は数秒から十数秒程度でrunningになるのですが、「Status Checks」は数分程度initializingで初期化処理を行っています。このステータスが共にpassedにならないとSSHアクセスでコケます。 今まで私が使っていたcheckタスクだと、「Instance State」のステータス1つしか確認していなかったので、後続タスクがSSHアクセスで中断したりしていました。それを回避するためのタスクが今回のcheckタスクになります。 task :check do run_locally do created_instances_list = 'CREATED_INSTANCES' def check_instance_status(instance_ids=[]) ec2 = AWS::EC2.new AWS.memoize do ec2info = ec2.client.describe_instance_status({'instance_ids' => instance_ids}) sys_status = ec2info.instance_status_set.map { |i| i.system_status.details[0].status } ins_status = ec2info.instance_status_set.map { |i| i.instance_status.details[0].status } status = sys_status + ins_status return status....Capistranoで新規作成したEC2インスタンスの初期設定
本項では、Capistranoで新規作成したEC2インスタンスへSSHで初回ログインした際の、保守用ユーザの作成、初期ユーザに対してのパスワード設定、サーバのホスト名設定など、いわゆるサーバ環境の初期設定を行うタスクを作ってみます。 その前に、ホスト名のつけ方としての色々と試してみての所感なのですが、初回SSHログイン後にそれぞれのインスタンスに対してHOSTS設定する際にEC2側にタグ付けてしていってもできるのですが、まずインスタンス作成する時にあらかじめホスト名の元となるタグを付けておいて、各インスタンスログイン後はそのタグを参照してHOST名を設定する方がスマートだと思いました。特に複数インスタンスを同時に立てる時などにホスト名に連番を振りたいとかいう時は、カウンター変数を使って回しているec2.instances.create()時にそのカウンターの数値を転用できるので簡単でした。ということで、インスタンス作成時にタグを追加する方法ですが、 # タグ情報 set :host_name, 'deploy-client' ~(中略)~ created_instances = [] cnt = 0 while cnt < fetch(:instance_count) do i = ec2.instances.create( ~(中略)~ ) sleep 10 while i.status == :panding i.tags['Name'] = [ fetch(:host_name), format("%02d", cnt+1) ].join('-') created_instances << i.id cnt += 1 end ~(省略)~ ── と、ec2.instances.create()の後でタグを付けてやればOKです1。 今回はこのNameタグの値をその後のタスクでインスタンスのホスト名として利用します。 いきなり横道に逸れましたが、本題に戻ります。 初回SSH時のタスクとして、前回作成したinitタスクを使います。流れとしては、デプロイサーバ側で新たに作成するユーザ用のキーペアを作成しておいて、デフォルトユーザにてSSHログイン後、まず保守用の新規ユーザアカウントを作成ます。その後、そのユーザに公開鍵認証によるSSH設定を行い、デフォルトユーザにはパスワードを設定してsudo権限を剥奪、ホスト名を設定して一旦ログアウトしています。 まず、タスク設定前に各種パラメータを定義します。...Capistranoで新規作成したEC2インスタンスにSSH接続する
前回に引き続き、Capistranoで新たに作成したEC2インスタンスにSSHでログインしてみます。 まず、事前準備として、AWS側で新規インスタンス用のキーペアを作成しておきます。AWSマネージメントコンソールの「EC2」メニューから「NETWORK & SECURITY」カテゴリの「Key Pairs」メニューで、キーペアを作成できるので、必要に応じて作成してください。本項の例では、「deploy-test」というCapistranoが稼動しているデプロイ環境用インスタンスで使用しているキーペアを使います。 そして、利用するキーペアのプライベートキーファイル(本項例ではdeploy-test.pemファイル)をCapistranoを実行するデプロイ環境のデプロイを実行するユーザのホームディレクトリ(本項例では/home/deploy-user/)にアップロードしておきます1。 アップロードしたプライベートキーには適切な読み込み権限を付与しておく必要があるので、ファイルのパーミッションを変更します。 $ cd ~ $ chmod 600 deploy-test.pem $ ls -l *.pem -rw------- 1 deploy-user deploy-user 1692 Jul 2 10:18 deploy-test.pem デプロイ設定ファイルconfig/deploy.rbは下記のように修正。 # config valid only for Capistrano 3.1 lock '3.2.1' # AWS SDK for Ruby を読み込む require 'aws-sdk' # AWS SDK用の設定 AWS.config({ :access_key_id => '<AWS ACCESS KEY ID>', :secret_access_key => '<AWS SECRET ACCESS KEY>', :region => 'ap-northeast-1', }) # AMIのimage_id # Amazon Linux AMI 2014....CapistranoでAWS EC2インスタンスをデプロイする時の注意点
前回のデプロイ設定ファイルで新たに作成されたEC2インスタンスには、キーペアやセキュリティグループなどが設定されていなかったため、そのままでは作成したインスタンスにSSHでアクセスできませんでした…1orz その後、色々とデプロイ設定を修正して、作成したEC2インスタンスにSSHでログインするところまで出来たので、その経緯を備忘録として書いてみた次第。 まぁ、CapistranoでAWSのEC2インスタンスを作成する際…というより、「AWS SDK for Ruby」でEC2インスタンスを作成する時の注意点…に近いのかもしれない。 まず、前回のconfig/deploy.rbからインスタンスラウンチのタスク部分を見てみる。 desc 'Launch an EC2 instance to each availability zone different' task :launch do run_locally do ec2 = AWS::EC2.new created_instances = [] cnt = 0 while cnt < fetch(:instance_count) do if cnt.even? then current_az = fetch(:availability_zones)[0] current_sn = fetch(:subnet_ids)[0] else current_az = fetch(:availability_zones)[1] current_sn = fetch(:subnet_ids)[1] end i = ec2....PV InstanceからHVM Instanceへ変換(CentOS6)
準備するもの ① EC2 Instance(CentOS6)既存の動いているものでもOK ② 変換元のRoot deviceのSnapshot ③ ②から作成したEBS Volume (SSDのほうが作業が早い) ③ 空のEBS Volume (SSDのほうが作業が早い) DEVICEの確認 # fdisk -l |grep dev Disk /dev/xvda: 10.7 GB, 10737418240 bytes Disk /dev/xvdb: 4289 MB, 4289200128 bytes Disk /dev/xvdf: 10.7 GB, 10737418240 bytes <-PV環境のRoot device Disk /dev/xvdg: 10.7 GB, 10737418240 bytes <-空のEBS Volume 作業開始 1.PVのDiskを縮小し、コピー容量を減らす...