Windows Azure command-lineと戯れる

Windows AzureUbuntu 12.04.3 LTSのVMを立ち上げるまでの手順を、公式のドキュメントを読みつつ、淡々とメモしていく。

Windows Azure command-lineはMacならばdmgをダウンロードできるようだったが、npmで自分で入れたほうが簡単そうだったので、npmで入れることにした。この場合、nvmなどを使ってnode.js, npmをインストールしておく必要がある。

$ npm install -g azure-cli

アカウントの設定

以下のコマンドを実行すると、ブラウザでアカウント情報ダウンロードが開き、必要な設定ファイルをダウンロードしてくれる。

$ azure account download

ダウンロードしたファイルをインポート。

$ azure account import ~/Downloads/MySubscription-credentials.publishsettings

VMの起動

目的のVMのイメージ名が分からないので、次のコマンドで確認した。

$ azure vm image list

結構な数のイメージがあるので、Canonicalの提供しているUbuntu LTSだけを列挙してみる。

$ azure vm image list | grep Canonical | grep LTS

結果として、今回僕が探しているイメージはb39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_3-LTS-amd64-server-20130827-en-us-30GBという名称であることが分かった。

ロケーション名も列挙して調べておく。

$ azure vm location list
info:    Executing command vm location list
+ Enumerating  locations
data:    Name
data:    --------------
data:    West US
data:    East US
data:    East Asia
data:    Southeast Asia
data:    North Europe
data:    West Europe
info:    vm location list command OK

各ロケーションのレイテンシーは特に調べていないのだが、今回はEast Asia(香港)を使うことにした。

イメージ名とロケーション名が分かったので、早速my-dns-nameというDNS名のVMを作って、ubuntuというユーザーでsshログインできるようにしてみる。

$ azure vm create my-dns-name \
  b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_3-LTS-amd64-server-20130827-en-us-30GB \
  ubuntu \
  --location "East Asia" \
  --ssh 22 \
  --ssh-cert path/to/cert.pem \
  --no-ssh-password

実際は1行で入力。cert.pemはこの記事を参考にして新規に証明書を作った。他のオプションは自明であろう。

VMが正常に作成されたならば、証明書と同時に生成したRSA秘密鍵を使ってSSHでログインできる。

$ ssh -i path/to/private.key ubuntu@my-dns-name.cloudapp.net

起動中のVM一覧を表示するならば、以下のとおり。

$ azure vm list

後は、Knife Soloで好みに調理してサーバーの設定は完了。実際はknife-azureを使うことになるだろうが、そこまではまだ試していない。また、実運用するならば、Affinity Group, Availability Setの設定必須だろうが、勉強不足でまだベストプラクティスがよく分かっていない。

Endpointを追加する(失敗)

Endpointを追加したいので、これもコマンドラインでやってみる。

が、以下のようなエラーが出て動かない。

% azure vm endpoint create my-dns-name --endpoint-protocol tcp --endpoint-name http 80 80
info:    Executing command vm endpoint create
+ Getting virtual machines
error:   errors is not defined
info:    Error information has been recorded to azure.err

これと同じ問題だと思って、修正がマージされたらしいバージョンも試してみたのだが、うーむ。

Endpointを追加する(成功)

azure vm endpoint --helpを見ていたら、create-multipleというコマンドもあることを知り、もしかしたらと思って試してみたら、こちらはちゃんと動いた。

$ azure vm endpoint create-multiple  my-dns-name 80
$ azure vm endpoint create-multiple  my-dns-name 443

endpoint一覧を取得するには、

$ azure vm endpoint list  my-dns-name
info:    Executing command vm endpoint list
+ Getting virtual machines
data:    Name              Protocol  Public Port  Private Port  Load Balanced
data:    ----------------  --------  -----------  ------------  -------------
data:    endp-443-443-tcp  tcp       443          443           No
data:    endp-80-80-tcp    tcp       80           80            No
data:    ssh               tcp       22           22            No
info:    vm endpoint list command OK

Endpoint名は勝手に、endp-<lb-port>-<vm-port>-の規約で命名されたようだ。本当は、http, httpsというEndpoint名にしたかったんだけど、まあ良しとしよう。

VMを削除する

基本的な操作は一通り試したので、作ったVMをバッサリ消す。

% azure vm delete --blob-delete my-dns-name
info:    Executing command vm delete
+ Getting virtual machines
Delete the VM debugcamp-10 ? [y/n] y
+ Deleting VM
+ Getting virtual machines
info:    Deleting image
info:    vm delete command OK

WordPressサイトのドメインを変更する

WordPressで公開したサイトのドメインを後から変更するというのはあまりやらない操作だとは思いますが、どうしてもドメインを移したいとか、公開済みのサイトをコピーしてテスト環境を作りたいといった理由でやらなくてはならないこともあると思います。

基本的には下記のドキュメントに書いてある通りのことをやればいいのでしょうが、長いし、「ピーエッチピーマイアドミンで…せよ」とか非本質的なことを書いてあったりするので、僕がいつもやっている手順をメモとして残しておきます。

マルチサイト機能が無効の場合

blog.example.comからblog.example.orgへのドメイン変更を行いたいとします。

まず、wp_optionsのsiteurlとhomeというキーにドメイン名が入った値があるはずなので、この2つの値を変更します。

mysql > update wp_options set option_value=replace(option_value, 'blog.example.com', 'blog.example.org') where option_name in ('siteurl', 'home');

これだけです。

マルチサイト機能が有効な場合

マルチサイト機能が有効な場合は、サイトごとに wp_2_options, wp_3_options, etc.というテーブルがあるはずなので、同様に変更します。

mysql > update wp_2_options set option_value=replace(option_value, 'blog.example.com', 'blog.example.org') where option_name in ('siteurl', 'home');

さらに、wp_blogsというテーブルがあるので、こちらも旧ドメインから新ドメインに変更します。

mysql > update wp_blogs set domain = 'blog.example.org' where domain = 'blog.example.com';

最後に、wp-config.php にドメインの設定があるはずなので、これを新ドメインに変更します。

define('DOMAIN_CURRENT_SITE', 'blog.example.org');

以上です。

ChefでUbuntu 12.04にemacs 24.3をインストールする

Ubuntu 12.04 LTSは、各種パッケージのバージョンやデフォルトの設定に安心感があり、「本当に楽だ、4, 5年前のCentOSと同じくらい鉄板だ」というありがたみを毎日噛み締めております。

しかしながら、多少バージョンが古くて不満を抱くパッケージもなくはありません。その一つが emacsです。

開発サーバーとして使っていると、どうしても最新版に近いのemacsを使いたいので、今まではソースからコンパイルして入れたり、こちらの記事を参考にして、PPAから最新版をインストールしていたりしました。

自分のためにインストールするならば、まあ手動で入れればいいと思っていましたが、このChef, Vagrant全盛時代において、vagrant upしたらemacsがインストールされていなくてはならない」 「全チームメンバーにemacsを使わせないといけない」 ということを考えると、Chefでインストールするようにしないといけないと思って、上記のgihyoの記事と同じことをするレシピを書きました。(opscodeのemacsのcookbookも観たのですが、見なかったことにしました。)

それが、これです。

apt_repository "cassou-emacs" do
  uri "http://ppa.launchpad.net/cassou/emacs/ubuntu"
  distribution node['lsb']['codename']
  components ["main"]
  keyserver "keyserver.ubuntu.com"
  key "CEC45805"
end

%w{emacs24-nox}.each do |pkg|
  package pkg do
    action :install
  end
end

Chefのレシピにしたおかげで、Windows AzureでVMを立てた後も一瞬でemacsをインストール出来ました!ChefとWindows Azureは素晴らしいですね!!(Windows Azureのすてま)

ChefとEmacsの話とは全然関係ないですが、最近初めてWindows AzureはWindows Serverだけではなく、Ubuntu等のLinux Serverも使えるということを知って、この頃はAWSではなくWindows Azureを使っています。(Windows Azureのすてま)

virtualenvwrapper-4.0を使う

emacsがあればあんまり開発環境には拘らない方なので、virtualenvwrapperもウノウラボにこの記事を書いたころから、ずっと同じバージョンを使い続けてきた。

アップデートしなかった理由は、いつからかvirtualevnwrapper単なるシェルスクリプトではなく、virtualenvwrapperというPythonモジュールになったので、どこに、どうやってインストールするのがベストなのか、考えるのが面倒だったせいもある。しかし、3秒くらい考えてみれば、全然面倒なことではなかった。

PATHが通っているところにvirtualenvがあるとする。僕の場合、最近はDebianUbuntuを使っているので、aptでvirtualenvを入れた。

$ sudo apt-get install virtualenv

その上で、virtualenvを使ってvirtualenvwrapperという環境を作って、そこにvirtualenvwrapperをインストールすることにした。

$ test -f ~/.virtualenvs/virtualenvwrapper/bin/python || virtualenv ~/.virtualenvs/virtualenvwrapper
$ ~/.virtualenvs/virtualenvwrapper/bin/pip install "virtualenvwrapper==4.0"

あとは、zshrc等でvirtualenvwrapper.shの設定をする。「virtualenvwrapperモジュールが見つからない!」と文句を言われないように、VIRTUALENVWRAPPER_PYTHONも設定しないといけない。

if [ -f ~/.virtualenvs/virtualenvwrapper/bin/python ]; then
  export WORKON_HOME=~/.virtualenvs
  export VIRTUALENVWRAPPER_PYTHON=~/.virtualenvs/virtualenvwrapper/bin/python
  source ~/.virtualenvs/virtualenvwrapper/bin/virtualenvwrapper.sh
fi

Pythonのパッケージ事情は何やら大きな変化が起こりつつあるらしいので、4, 5年前のvirtualevnwrapperからようやくアップデートできて、一つ肩の荷が下りた。

PR

Appiumを使ってMobile Safari上でSeleniumのテストケースを実行する

SeleniumiPhone DriverはDEPRECATEDらしいので、代替として紹介されている Appium を試してみた。

Appium is an open source test automation framework for use with native and hybrid mobile apps. It drives iOS and Android apps using the WebDriver JSON wire protocol.

http://appium.io/

最初は、AppiumはSelenium WebDriverとしてWebのテストだけに使えるものだと思っていたが、実はiOS/AndroidのネイティブUIの自動操作・自動テストにも使える(むしろそっちがメイン)ということを知って、さらに興味をそそられた。

が、ドキュメントが少なく、地雷があり過ぎる印象があるので、軽くメモを残しておく。環境はMacを想定しています。

インストール

公式のドキュメントにはdmgを使う方法と、npmで入れる方法の二通りが記載されているが、どっちもどっちという感じがする。

dmgを使うとAppium.appというGUIアプリをインストールできるが、これはnodeとnode_modulesをラッピングしたごく単純なアプリ。

$ nvm install v0.10.7
$ npm install -g appium

でnpmでインストールしたのと実質的に同じものであるはずなのだが、npmで入れると後述のMobile SafariのテストがInstrumentsのタイムアウトで動かなかったので、何か手順が不足しているのかもしれない。

dmgの方のソースはこれっぽい。

iOSアプリをテストする

Appium.appを使う場合は、App Pathにアプリのパスを指定する。

アプリのパスはXcodeデバッグビルドした場合には、

~/Library/Application Support/iPhone Simulator/6.0/Applications/049AE569-A182-439A-BA3A-36D02DB4BB09/TestApp.app

のようなパスになると思う。

アプリを指定したら、"Lauch"ボタンでSeleniumのRemoteWebDriverが起動する。

その上で、Appiumのサンプルコードを参考に、次のような感じでテストを書く。

import unittest
import os
from random import randint
from selenium import webdriver


class TestSequenceFunctions(unittest.TestCase):
    def setUp(self):
        # set up appium
        self.driver = webdriver.Remote(
            command_executor='http://127.0.0.1:4723/wd/hub',
            desired_capabilities={
                'browserName': 'iOS',
                'platform': 'Mac',
                'version': '6.0',
            })
        self._values = []

    def _populate(self):
        # populate text fields with two random number
        elems = self.driver.find_elements_by_tag_name('textField')
        for elem in elems:
            rndNum = randint(0, 10)
            elem.send_keys(rndNum)
            self._values.append(rndNum)

    def test_ui_computation(self):
        # populate text fields with values
        self._populate()
        # trigger computation by using the button
        buttons = self.driver.find_elements_by_tag_name("button")
        buttons[0].click()
        # is sum equal ?
        texts = self.driver.find_elements_by_tag_name("staticText")
        self.assertEqual(int(texts[0].text), self._values[0] + self._values[1])

    def tearDown(self):
        self.driver.quit()


if __name__ == '__main__':
    unittest.main()

Mobile Safariでテストを実行する

いくつか地雷がある。

npmでインストールしたappiumに接続してもタイムアウトになる
$ npm install -g appium
$ appium --force-iphone --safari -a 0.0.0.0 -p 4723
info: Appium REST http interface listener started on 0.0.0.0:4723

でAppium.appと同じことをやっていると思え、実際にSelenium RemoteWebDriverで接続しに行っても処理がスタートした雰囲気があるのだが、Instrumentsのタイムアウト?でテストが実行できない。

Appium.appで頑張る方針に切り替えたのであまり深追いしていない。

配布されているAppium.appにはバグがあるかもしれない

現時点での最新版0.5.2のAppium.appでMobile Safariを起動しようとすると、

Could not find mobile safari with version '6.0': Error: EACCES, mkdir '/tmpAppium-MobileSafari.app'

というエラーになる。これはgithubのIssue 635として報告されている。

すでに修正済みなのでdmgも修正されるだろうが、僕は手っ取り早く直したかったので、バンドル内のファイルに、

/Applications/Appium.app/Contents/Resources/node_modules/appium/app/helpers.js

このコミットと同じ修正を加えた。

appにsafariを指定しなくてはいけない

Safariを起動するには、次のどれかの方法でappにsafariを指定しなくてはいけない。

  • desired_capabilitiesに'app': 'safari'を追加する
  • Appium.appを使う場合
    • Appium.appの"Use Mobile Safari"にチェックを入れる
  • npmのappiumを使う場合
    • "--safari" オプションをつけてappiumを起動する
driver.quit() で例外が発生する

地雷を乗り越えてようやくテストを実行できるところまで来ても、tearDownに書いた driver.quit() で例外が発生して、毎回Appiumのプロセスが死んでしまう。これも深追いしていない。

# -*- coding: utf-8 -*-
import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class MobileSafariTest(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Remote(
            command_executor='http://127.0.0.1:4723/wd/hub',
            desired_capabilities={
                'platform': 'Mac',
                'device': 'iPhone Simulator',
                'version': '6.1',
                'app': 'safari'
                })

    def tearDown(self):
        try:
            self.driver.quit()
        except:
            pass

    def test_title(self):
        self.driver.get('http://m.yahoo.co.jp/')

        input_element = self.driver.find_element_by_name('p')
        input_element.send_keys("Appium")
        input_element.submit()

        WebDriverWait(self.driver, 10).until(EC.title_contains("Appium"))
        self.assertRegexpMatches(self.driver.title, r'^Appium')

if __name__ == '__main__':
    unittest.main()

まとめ

  • iOSアプリ、Androidアプリの自動操作、自動テストにはAppiumは有望そう
  • ドキュメントが少なく、バグなのか、使い方が間違っているのか分からないことが多い点がキツイ

BrunchでChaplinアプリのプロジェクトを作る

プロジェクトの作成

brunchをインストールする。

$ npm install -g brunch

brunch-with-chaplinをスケルトンにしてプロジェクトを作成。

$ brunch new myproject --skeleton=git://github.com/paulmillr/brunch-with-chaplin.git

プロジェクトディレクトリに必要なnode_moduelsをインストールし*1、`brunch w` でbrunchを起動する。

$ cd myproject
$ npm install -d
$ brunch w

brunchのスタンドアローンサーバを使うには、`-s` オプションを使う。

$ brunch w -s

プロジェクト内のCoffeeScriptやテンプレートを変更すると、brunchが勝手にビルドしてくれる。便利。

Underscore.jsをLo-Dashに変更

これは好みもあると思うが、Underscore.jsではなくて、Lo-Dashを使いたいので、vendor以下にあるスクリプトを入れ替える。

githubから最新版のLo-Dashをダウンロード。

$ curl -o vendor/scripts/lodash.underscore-1.0.1.js https://raw.github.com/bestiejs/lodash/v1.0.1/dist/lodash.underscore.js

プロジェクト直下のconfig.coffeeにあるunderscoreをLo-Dashのものに置き換える。

% git diff config.coffee 
diff --git a/config.coffee b/config.coffee
index db9d206..d10c363 100644
--- a/config.coffee
+++ b/config.coffee
@@ -13,7 +13,7 @@ exports.config =
         before: [
           'vendor/scripts/console-polyfill.js',
           'vendor/scripts/jquery-1.9.1.js',
-          'vendor/scripts/underscore-1.4.4.js',
+          'vendor/scripts/lodash.underscore-1.0.1.js',
           'vendor/scripts/backbone-0.9.10.js'
         ]
         after: [

*1:これはbranch newでプロジェクトを作った直後はいらないかもしれないが、gitでcloneした直後には必要

Riak Source Code Reading #1に参加しました

昨日開催された「Riak Source Code Reading @東京 #1」に参加しました。そして、Riakのbitcaskバックエンドとeleveldbバックエンドのコードに関して発表してきました。

その時のスライドがこちら

スライドといってもたいした事は書いてなくて、発表中にポイントとなるコードに集中するために、元々のコードをコピー&ペーストして整形しただけと言っていいものです。コードリーディングの対象としたのは、ほぼ次の2ファイルのみ。

正直、Riakのメインの処理というよりは、別ライブラリを呼び出すためのグルーコードという感じで、どういうコードリーディングになるか不安だったのですが、予想以上に発表中に質問が出て、個人的には発見があったり、次に繋がる内容だったのではないかと思っております。当日全く仕事をしないで資料を作った業務時間の合間を縫って資料を仕上げたかいがあった!

以下、強調しておきたいところや反省点。

ソースコードより先にドキュメントを読もう

自分自身、ドキュメントよりソースコードを先に読んで後からちゃんとドキュメントに書いてあったと気づくことが多々あるのですが、今回もそういう経験がありました。grepであたりをつけていったり、逐一処理を追っていくよりは、ドキュメントからあたりを付けていったほうが早かったこともありますね。今回だとBitcaskのMergeとSyncとか。

コードリーディングの上では、やはりConceptsのページが重要に感じます。

Riakの開発を始めるならば、次の2つあたり。特にkerlの辺りは、今回発表するにあたり下調べをしていて初めて知ったのですが、特に重要。

Riakの実運用やチューニングを始めるならば、このあたり。

HanoiDB

大変興味深いスライドなので再掲。必見だと思います。

Pandoc

今回発表するにあたり、kuenishi氏のRiak Source Code Reading @TokyoのgithubレポジトリにPull Requestする形式で資料を用意したいなと思っていたのですが、MarkdownからHTMLのスライドにするのはどういう方法が一般的なのか知らなくて少し悩みました。

空前のHaskellマイブーム、もとい、Haskellは本当に流行っているので、空前のHaskellブームということもあり、"markdown slide Haskell"でググって、PandocでHTMLスライド化できるということを今回初めて知りました。

PandocのREADMEを参考にして、

$ pandoc -s -t slidy -i --self-contained -o output.html input.md 

というオプションで簡単にスライドに出来た!素晴らしい!

ErlangのSyntax Highlightが崩れてしまうようなことがあったので、Syntax Highlightを使わなかったのですが、そのせいでスライドでは多少コードが読みにくくなっていたかもしれません。それが反省点。

良かった点

久しぶりに会社の外で発表とかしましたが、うっかり「Riakを実際にプロダクションで使う人なんているはずないじゃないですか」とか口走らなくて、本当に良かったと思います。(たぶん言わなかったと思う。)