page_adsence

2015年12月28日月曜日

Laravel4系のパッケージ作成における設定ファイルの読み込み

Laravelでcomoserで管理する自作ライブラリを作成していたのですが、どうしても設定ファイルが読み込めないという事態があった。

下記の様な形で設定ファイルを置いてみたのですが、いくらやってもNULL。
app/config/packages/vendor_name/package_name/config.php
app/config/packages/vendor_name/package_name/test.php

色々調べて見ると、原因はパッケージ側に設定ファイルがない事が原因でした。
vendor/vendor_name/package_name/src/vendor_namespace/config/config.php
vendor/vendor_name/package_name/src/vendor_namespace/config/test.php

どうやらパッケージ側の設定ファイルの値を、アプリ側の設定ファイルの値で上書きするような挙動になっているようです。 使用する場合は、
Config::get('package_name::filename.key')


例えば、下記の様な設定ファイルがあるとする。
$ vi vendor/vendor_name/package_name/src/vendor_namespace/config/test.php
<?php
return array(

    'key' => 'testtest'

);
$ vi app/config/packages/vendor_name/package_name/test.php
<?php
return array(

    'key' => 'testtesttest'

);


このファイルの内容を取得する場合は、
Config::get('package_name::test.key'); → 結果:testtesttest
設定ファイル名がconfig.phpで、アプリ側に設定ファイルがなかった場合
$ vi vendor/vendor_name/package_name/src/vendor_namespace/config/config.php
<?php
return array(

    'key' => 'testtest'

);
app/config/packages/vendor_name/package_name/config.php ← このファイルはない
このファイルの内容を取得する場合は、
Config::get('package_name::key'); → 結果:testtest

Dockerを使って開発環境を整えてみた

今まで自分の開発環境はWindows7 + Cygwin + Vagrant + Ansible + VirtualBoxで作っていたのですが、
マシンスペックの都合上、複数台のVMを動かすのが厳しくなってきたので、Dockerを使って開発環境を構築してみようと思います。
また、DockerHubからイメージを落としてくるのではなく、自分で1からDockerfileを作っていこうと思います。
とりあえず1コンテナにつき1つのミドルウェアのみインストールするようにしました。
また、ベースOSはすべてCentOS6.7にしました。

今回構築したい環境は下記のような感じです。
・ミドルウェア
  - Nginx 1.8.0(LB) × 1台
  - Nginx 1.8.0 × 2台
  - PHP 5.6系
  - MySQL 5.5系
  - HAProxy 1.5系
  - Redis 3.0.5 × 3台(master 1台、slave 2台)
  - Redis-Sentinel × 3台

・構成図


まず、それぞれのDockerfileから作成していきます。
Dockerfileの作成手順としては、下記の様な感じで作っていきました。

1.ベースイメージを取得
$ docker pull centos:6.7
2.ベースのイメージからコンテナを作成してコンテナ内部に入る
$ docker run -it centos:6.7 bash
3.コンテナ内部で、Dockerfileに書きたい内容を一通り試す

4.コンテナから出る
# exit
5.Dockerfileに書く

6.buildしてみる
docker build -t [IMAGE_NAME] PATH
7.成功したらデーモンモードで起動させる。
docker run -d [IMAGE]
8.失敗した場合はログを確認する
docker logs [CONTAINER]


上記の手順で作成したDockerfileは下記にあげてあります。
但し、解説用に編集したDockerfileなので、そのままの状態では使用出来ないので予めご了承下さい。

https://github.com/gyagya/Dockerfiles


続いては、このDockerfileを使って、イメージを作成していきます。
docker build -t image_name Dockerfile

作成したイメージの一覧を確認します。
$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
php                  latest              ab774aab6741        2 days ago          477.8 MB
nginx                latest              3cb45e85b756        4 days ago          474.2 MB
nginx-lb             latest              c0e293e58e56        5 days ago          474.1 MB
mysql-data-storage   latest              85e7b48d7503        5 days ago          190.6 MB
mysql                latest              528877b6d10d        5 days ago          781.2 MB
haproxy              latest              8171746ff33b        5 days ago          473.9 MB
redis-sentinel       latest              d90eb8fa284b        5 days ago          503.4 MB
redis-slave          latest              7b0ca34ff4b7        5 days ago          503.4 MB
redis-data-storage   latest              518bace5a446        5 days ago          190.6 MB
redis                latest              23c44601fccd        5 days ago          503.4 MB
file-storage         latest              ab96aa28dc9e        5 days ago          190.6 MB
centos               centos6.7           3fba1048142f        8 weeks ago         190.6 MB

ここまで揃った段階で、docker-composeを使用してまとめてコンテナを起動させます。
docker-composeのインストール方法は下記の通りです。
$ sudo curl -L https://github.com/docker/compose/releases/download/1.4.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
インストールが完了したらdocker-compose.ymlがあるディレクトリか、その配下のディレクトリで
$ docker-compose up -d
で起動ができます。
起動しているかどうかを確認する。
$ docker-compose ps
ここまで完了すれば環境構築は完了です。

■Dockerfileを作成するにあたっての注意点が幾つか。
1.Dockerは何かしらがフォアグラウンドで起動していないと、コンテナを起動させてもすぐに終了してしまいます。
2.資源の節約という意味で、Dockerfileはなるべく容量を少なくする必要が有ります。
下記2つのイメージは同じ内容のコンテナで、Dockerfileの書き方を変えただけのものになります。
$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mysql-mroonga2         latest              697b08894cce        29 seconds ago      781.2 MB
mysql-mroonga          latest              945337cb1dc4        12 hours ago        1.242 GB
何が違うのかというと、容量が少ない方でやった内容はRUNの回数を減らす。yum install後はyum clean allするなどです。
DockerはRUN単位でファイルシステムの更新が走るので、ゴミファイルがあるとその分無駄に差分が発生してしまい、容量の肥大化につながります。

■データの永続化とポータビリティについて
Dockerでデータの永続化をしたコンテナ(MySQLのデータコンテナ等)をexport/saveコマンドとかで持ち運びたいと思ったのですが、これは全く持って出来ません。
永続化(コンテナが停止状態でもファイルはいじれる)は出来ます。
コンテナのexportするとコンテナ自体は移動出来ます。
但し、中身のデータは一切移動出来ません。
おそらく将来的にも出来る様になることはないと思います。
Dockerはあくまで環境のポータビリティを上げるだけで、データのポータビリティを上げるためのツールではないからっていうことなのかもしれません。

■ファイルのマウントに関して 現状使用しているdocker1.7系では、ファイルのマウントはホスト側からコンテナ側にマウントすることしか出来ません。
今回作った環境をDockerをあまり知らない人でも使えるように、コンテナに入らずにログファイルや設定ファイルをいじれる様にしていました。
しかし、ホスト側からコンテナ側にマウントすることしか出来ず、ホスト側で空のディレクトリを用意しておき、設定ファイルが置いてあるディレクトリをマウントしてしまうと、
当然コンテナ側の設定ファイルが消えるので起動しないといった事が有りました。
仕方ないので、コンテナ側と同じファイルをホストマシン上に置いて対応しましたが、設定ファイルを2重管理しないといけないので非常に面倒くさいです。
これはなんとかしたいのですが、今のところ上記の方法でしか解決出来ていません。

Laravel4系のカスタムバリデートの作り方

基本的な作り方に関しては公式サイトに書いてあるのですが、日本語で残しておきたかったので書いておきます。

https://laravel.com/docs/4.2/validation#custom-validation-rules

カスタムバリデータクラスの設置場所は自由で、今回はcomposerで管理する用のライブラリを作成したので、下記のディレクトリに配置しました。
/vendor/vendor_name/package_name/src/vendor_namespace/Validator/CustomValidator.php
<?php namespace TEST\Validator;

class CustomValidator extends \Illuminate\Validation\Validator {

    public function validateTest($attribute, $value, $parameters)
    {
        // バリデート処理
        // OKの場合はtrueを返す
        
        // NGの場合はfalseを返す
    }

    public function replaceTest($message, $attribute, $rule, $parameters)
    {
        // バリデートNGの時に表示するメッセージを編集する
        return $message;
    }

}

元になるルールを作成
$ vi /vendor/vendor_name/package_name/src/vendor_namespace/config/rules.php
<?php
return array(

    // 入力チェック
    'input_rules' => array(
        'name'    => 'required|test',
        'email'   => 'required',
        'message' => 'max:2000',
    ),
);

元になるメッセージを作成
$ vi /vendor/vendor_name/package_name/src/vendor_namespace/config/messages.php
<?php
return array(

    'test' => 'エラーです。',

);

利用する側は下記の様に記述する。
$rules = Config::get('package_name::rules.input_rules');
$messages = Config::get('package_name::messages.test');
$validator = \Validator::make(Input::all(), $rules, $messages);
if ($validator->fails()) {
    var_dump($validator->messages());
}
さらに下記ファイルの最後に追記する。
vi app/start/global.php

/**
 * カスタムバリデータの登録
 */
Validator::resolver(function($translator, $data, $rules, $messages)
{
    return new Test\Validator\CustomValidator(
        $translator, $data, $rules, $messages);
});
以上で使える様になるはず。

2015年11月24日火曜日

CentOS6系とCentOS7にDockerをインストール

CentOS6系と、CentOS7系にそれぞれDockerをインストールしてみました。
現時点でDockerの最新バージョンは1.8系ですが、CentOS6系だと、Docker1.7系までしか使えませんので、
最新版のDockerを使いたい場合は、自動的にCentOS7系を使うことになります。

CentOS6.7にDockerをインストール

まず、面倒な方から。
CentOS6系でDockerをインストールするためには、EPELのリポジトリを追加して、そこからインストールする必要があります。
但し、追加したEPELリポジトリ内にある、dockerというのは全くの別物なので要注意。
早速、EPELのリポジトリを追加します。
sudo rpm -ivh http://ftp.jaist.ac.jp/pub/Linux/Fedora/epel/6/i386/epel-release-6-8.noarch.rpm

インストールしたくない方のdockerの情報を確認。
$ yum info docker
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
epel/metalink                                                                      | 5.5 kB     00:00
 * base: ftp.yz.yamagata-u.ac.jp
 * epel: ftp.kddilabs.jp
 * extras: ftp.yz.yamagata-u.ac.jp
 * updates: ftp.yz.yamagata-u.ac.jp
epel                                                                               | 4.3 kB     00:00
epel/primary_db                                                                    | 5.7 MB     00:03
利用可能なパッケージ
名前                : docker
アーキテクチャ      : x86_64
バージョン          : 1.5
リリース            : 5.el6
容量                : 19 k
リポジトリー        : epel
要約                : KDE and GNOME2 system tray replacement docking application
URL                 : http://icculus.org/openbox/2/docker/ ← URLが全然別物。要約も全く別物
ライセンス          : GPL+
説明                : Docker is a docking application (WindowMaker dock app) which acts as a system
                    : tray for KDE and GNOME2. It can be used to replace the panel in either
                    : environment, allowing you to have a system tray without running the KDE/GNOME
                    : panel or environment.

CentOS6.7の場合だとdocker-ioというのが、インストールしたいDockerになります。

 yum info docker-io
読み込んだプラグイン:fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.yz.yamagata-u.ac.jp
 * epel: ftp.kddilabs.jp
 * extras: ftp.yz.yamagata-u.ac.jp
 * updates: ftp.yz.yamagata-u.ac.jp
利用可能なパッケージ
名前                : docker-io
アーキテクチャ      : x86_64
バージョン          : 1.7.1
リリース            : 2.el6
容量                : 4.6 M
リポジトリー        : epel
要約                : Automates deployment of containerized applications
URL                 : http://www.docker.com ← Docker公式サイトのURLになっている
ライセンス          : ASL 2.0
説明                : Docker is an open-source engine that automates the deployment of any
                    : application as a lightweight, portable, self-sufficient container that will
                    : run virtually anywhere.
                    :
                    : Docker containers can encapsulate any payload, and will run consistently on
                    : and between virtually any server. The same container that a developer builds
                    : and tests on a laptop will run at scale, in production*, on VMs, bare-metal
                    : servers, OpenStack clusters, public instances, or combinations of the above.

docker-ioが正しい事を確認したので、yumでインストールする。
$ sudo yum install docker-io
読み込んだプラグイン:fastestmirror
インストール処理の設定をしています
Determining fastest mirrors
epel/metalink                                                                      | 5.5 kB     00:00
 * base: www.ftp.ne.jp
 * epel: ftp.kddilabs.jp
 * extras: www.ftp.ne.jp
 * updates: www.ftp.ne.jp
base                                                                               | 3.7 kB     00:00
base/primary_db                                                                    | 4.6 MB     00:03
epel                                                                               | 4.3 kB     00:00
epel/primary_db                                                                    | 5.7 MB     00:03
extras                                                                             | 3.4 kB     00:00
extras/primary_db                                                                  |  33 kB     00:00
updates                                                                            | 3.4 kB     00:00
updates/primary_db                                                                 | 2.6 MB     00:01
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> Package docker-io.x86_64 0:1.7.1-2.el6 will be インストール
--> 依存性の処理をしています: xz のパッケージ: docker-io-1.7.1-2.el6.x86_64
--> 依存性の処理をしています: lxc のパッケージ: docker-io-1.7.1-2.el6.x86_64
--> 依存性の処理をしています: libcgroup のパッケージ: docker-io-1.7.1-2.el6.x86_64
--> トランザクションの確認を実行しています。
---> Package libcgroup.x86_64 0:0.40.rc1-16.el6 will be インストール
---> Package lxc.x86_64 0:1.0.7-4.el6 will be インストール
--> 依存性の処理をしています: lua-lxc(x86-64) = 1.0.7-4.el6 のパッケージ: lxc-1.0.7-4.el6.x86_64
--> 依存性の処理をしています: lua-alt-getopt のパッケージ: lxc-1.0.7-4.el6.x86_64
--> 依存性の処理をしています: liblxc.so.1()(64bit) のパッケージ: lxc-1.0.7-4.el6.x86_64
---> Package xz.x86_64 0:4.999.9-0.5.beta.20091007git.el6 will be インストール
--> トランザクションの確認を実行しています。
---> Package lua-alt-getopt.noarch 0:0.7.0-1.el6 will be インストール
---> Package lua-lxc.x86_64 0:1.0.7-4.el6 will be インストール
--> 依存性の処理をしています: lua-filesystem のパッケージ: lua-lxc-1.0.7-4.el6.x86_64
---> Package lxc-libs.x86_64 0:1.0.7-4.el6 will be インストール
--> 依存性の処理をしています: rsync のパッケージ: lxc-libs-1.0.7-4.el6.x86_64
--> トランザクションの確認を実行しています。
---> Package lua-filesystem.x86_64 0:1.4.2-1.el6 will be インストール
---> Package rsync.x86_64 0:3.0.6-12.el6 will be インストール
--> 依存性解決を終了しました。

依存性を解決しました

==========================================================================================================
 パッケージ              アーキテクチャ  バージョン                                   リポジトリー   容量
==========================================================================================================
インストールしています:
 docker-io               x86_64          1.7.1-2.el6                                  epel          4.6 M
依存性関連でのインストールをします。:
 libcgroup               x86_64          0.40.rc1-16.el6                              base          129 k
 lua-alt-getopt          noarch          0.7.0-1.el6                                  epel          6.9 k
 lua-filesystem          x86_64          1.4.2-1.el6                                  epel           24 k
 lua-lxc                 x86_64          1.0.7-4.el6                                  epel           15 k
 lxc                     x86_64          1.0.7-4.el6                                  epel          120 k
 lxc-libs                x86_64          1.0.7-4.el6                                  epel          252 k
 rsync                   x86_64          3.0.6-12.el6                                 base          335 k
 xz                      x86_64          4.999.9-0.5.beta.20091007git.el6             base          137 k

トランザクションの要約
==========================================================================================================
インストール         9 パッケージ

総ダウンロード容量: 5.6 M
インストール済み容量: 22 M
これでいいですか? [y/N]y
パッケージをダウンロードしています:
(1/9): docker-io-1.7.1-2.el6.x86_64.rpm                                            | 4.6 MB     00:03
(2/9): libcgroup-0.40.rc1-16.el6.x86_64.rpm                                        | 129 kB     00:00
(3/9): lua-alt-getopt-0.7.0-1.el6.noarch.rpm                                       | 6.9 kB     00:00
(4/9): lua-filesystem-1.4.2-1.el6.x86_64.rpm                                       |  24 kB     00:00
(5/9): lua-lxc-1.0.7-4.el6.x86_64.rpm                                              |  15 kB     00:00
(6/9): lxc-1.0.7-4.el6.x86_64.rpm                                                  | 120 kB     00:00
(7/9): lxc-libs-1.0.7-4.el6.x86_64.rpm                                             | 252 kB     00:00
(8/9): rsync-3.0.6-12.el6.x86_64.rpm                                               | 335 kB     00:00
(9/9): xz-4.999.9-0.5.beta.20091007git.el6.x86_64.rpm                              | 137 kB     00:00
----------------------------------------------------------------------------------------------------------
合計                                                                      1.2 MB/s | 5.6 MB     00:04
警告: rpmts_HdrFromFdno: ヘッダ V3 RSA/SHA256 Signature, key ID 0608b895: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
Importing GPG key 0x0608B895:
 Userid : EPEL (6) 
 Package: epel-release-6-8.noarch (installed)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
これでいいですか? [y/N]y
warning: rpmts_HdrFromFdno: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
Importing GPG key 0xC105B9DE:
 Userid : CentOS-6 Key (CentOS 6 Official Signing Key) 
 Package: centos-release-6-7.el6.centos.12.3.x86_64 (@anaconda-CentOS-201508042137.x86_64/6.7)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
これでいいですか? [y/N]y
rpm_check_debug を実行しています
トランザクションのテストを実行しています
トランザクションのテストを成功しました
トランザクションを実行しています
警告: RPMDB は yum 以外で変更されました。
  インストールしています  : lua-filesystem-1.4.2-1.el6.x86_64                                         1/9
  インストールしています  : libcgroup-0.40.rc1-16.el6.x86_64                                          2/9
  インストールしています  : lua-alt-getopt-0.7.0-1.el6.noarch                                         3/9
  インストールしています  : xz-4.999.9-0.5.beta.20091007git.el6.x86_64                                4/9
  インストールしています  : rsync-3.0.6-12.el6.x86_64                                                 5/9
  インストールしています  : lxc-libs-1.0.7-4.el6.x86_64                                               6/9
  インストールしています  : lua-lxc-1.0.7-4.el6.x86_64                                                7/9
  インストールしています  : lxc-1.0.7-4.el6.x86_64                                                    8/9
  インストールしています  : docker-io-1.7.1-2.el6.x86_64                                              9/9
  Verifying               : lua-lxc-1.0.7-4.el6.x86_64                                                1/9
  Verifying               : rsync-3.0.6-12.el6.x86_64                                                 2/9
  Verifying               : lxc-1.0.7-4.el6.x86_64                                                    3/9
  Verifying               : xz-4.999.9-0.5.beta.20091007git.el6.x86_64                                4/9
  Verifying               : docker-io-1.7.1-2.el6.x86_64                                              5/9
  Verifying               : lxc-libs-1.0.7-4.el6.x86_64                                               6/9
  Verifying               : lua-alt-getopt-0.7.0-1.el6.noarch                                         7/9
  Verifying               : libcgroup-0.40.rc1-16.el6.x86_64                                          8/9
  Verifying               : lua-filesystem-1.4.2-1.el6.x86_64                                         9/9

インストール:
  docker-io.x86_64 0:1.7.1-2.el6

依存性関連をインストールしました:
  libcgroup.x86_64 0:0.40.rc1-16.el6              lua-alt-getopt.noarch 0:0.7.0-1.el6
  lua-filesystem.x86_64 0:1.4.2-1.el6             lua-lxc.x86_64 0:1.0.7-4.el6
  lxc.x86_64 0:1.0.7-4.el6                        lxc-libs.x86_64 0:1.0.7-4.el6
  rsync.x86_64 0:3.0.6-12.el6                     xz.x86_64 0:4.999.9-0.5.beta.20091007git.el6

完了しました!

以上でインストール自体は終了。
後はDockerをchkconfigの設定と起動をするのですが、dockerはインストール状態だとrootユーザーでないと利用出来ないので、dockerグループに現状のユーザーを追加します。
$ sudo chkconfig docker on
$ sudo service docker start
試しに一般ユーザーでdockerコマンドを叩いてみると下記の様なエラーが出る。
$ docker ps
Get http:///var/run/docker.sock/v1.19/containers/json: dial unix /var/run/docker.sock: permission denied. Are you trying to connect to a TLS-enabled daemon without TLS?
sudoを使えば問題ない。
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

毎回sudo使うのは嫌なので、docker用のグループに現在のユーザーを追加する。
$ sudo groupadd docker
$ sudo gpasswd -a $USER docker
$ sudo service docker restart
で、一回ターミナルから抜けて、再ログインする。
以上でCentOS6系へのインストール関連の作業は完了。



CentOS7にDockerをインストール

CentOS7へのDockerのインストールは非常に簡単でした。
CentOS6系と違い、特にリポジトリを追加することなく、インストールが可能です。

念のため確認します。
$ yum info docker
読み込んだプラグイン:fastestmirror
Determining fastest mirrors
 * base: www.ftp.ne.jp
 * extras: www.ftp.ne.jp
 * updates: www.ftp.ne.jp
利用可能なパッケージ
名前                : docker
アーキテクチャー    : x86_64
バージョン          : 1.8.2
リリース            : 7.el7.centos
容量                : 8.7 M
リポジトリー        : extras/7/x86_64
要約                : Automates deployment of containerized applications
URL                 : https://github.com/docker/docker ← ちゃんとdockerのURLになっている
ライセンス          : ASL 2.0
説明                : Docker is an open-source engine that automates the deployment of any
                    : application as a lightweight, portable, self-sufficient container that will
                    : run virtually anywhere.
                    :
                    : Docker containers can encapsulate any payload, and will run consistently on
                    : and between virtually any server. The same container that a developer builds
                    : and tests on a laptop will run at scale, in production*, on VMs, bare-metal
                    : servers, OpenStack clusters, public instances, or combinations of the above.

dockerをインストールして、サービス起動する。
$ sudo yum install docker
$ sudo systemctl start docker
また、先ほどと同様にdockerグループを追加して、現在のユーザーをdockerグループに追加し、再ログインする。
$ sudo groupadd docker
$ sudo gpasswd -a $USER docker
$ sudo systemctl restart docker

以上でCentOS7へのインストールも完了です。
どちらのバージョンでもインストールの手間は大したことないので、どちらを選んでも問題ないと思います。
しかし、Dockerの開発スピードはかなり早いので、なるべく最新版を使った方がいいのかもしれません。

2015年11月12日木曜日

Gitの最初のコミットを取り消す

なんか結構な回数最初のコミットをやり直している気がする。
で、毎回忘れてググっているのでメモ。

$ git update-ref -d HEAD

最初ではない、直前のコミットを取り消す
$ git reset --soft HEAD^

2015年11月10日火曜日

iOS9でGmailAPIを使ってみる

iOSアプリでGmailを受信してみたかったので、とりあえずチュートリアルをやってみた。

https://developers.google.com/gmail/api/quickstart/ios?hl=ja&ver=swift

ちなみにこのチュートリアル通りにやると動きません。
iOS9が出たばかりの時にやり始めたので、iOS初心者の自分としては、チュートリアル通りに書いても動かないという状態から抜け出すのに、かなり時間を取られました。
上記のサイトは英語で書かれているので、英語に自信のある方はどうぞ。
そうじゃない方は下記のサイトが参考になります。
ただし、参考にするのは「Gmail APIを有効化する」するところまで。

http://hazakurakeita.hatenablog.com/entry/2015/08/26/Swift%E3%81%A7Gmail_API%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95_/_Gmail_API_iOS_Quickstart

それ以降に関してはCocoaPodsを使ってやった方が早いので、今回はCocoaPodsを使ってやっていきます。
CocoaPodsのインストールがまだ済んでいない方は、各自インストールしておいてください。

では、GmailAPIを有効化して、クライアントIDとクライアントシークレットが確認できたところから開始します。

まずXcodeで適当なプロジェクトを作成します。
自分は「gmailTutorial」というSingleViewApplicationのプロジェクトを作成しました。
作成したディレクトリは下記の通り。

/Users/user_name/Document/iPhoneApps/gmailTutorial

プロジェクトを作成したら一旦作ったプロジェクトは閉じます。

作成したプロジェクトのディレクトリへ移動し、Podfileを作成します。

cd /Users/user_name/Document/iPhoneApps/gmailTutorial
vi Podfile
platform :ios, "7.0"
pod 'Google-API-Client'

上記まで書けたら保存して閉じます。
で、インストール。

$ pod install
Updating local specs repositories
Analyzing dependencies
Downloading dependencies
Installing Google-API-Client (1.0.422)
Installing gtm-http-fetcher (1.0.141)
Installing gtm-oauth2 (1.0.126)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `gmailTutorial.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 3 total
pods installed.

現在の階層に作成されている「gmailTutorial.xcworkspace」をXcodeで開きます。
で、プロジェクトナビゲーターの、
Pods → Pods → Google-API-Client → Gmail → GTLGmail.h

gmailTutorial → gmailTutorial → GTLGmail.h
にドラッグアンドドロップする。
下記のような画面が出てくるが、そのままfinishボタンを押す。



続いて、
Pods → Pods → gtm-oauth2 → GTMOAuth2ViewControllerTouch.h

gmailTutorial → gmailTutorial → GTMOAuth2ViewControllerTouch.h
にドラッグアンドドロップする。
先ほどと同じく、そのままfinishボタンを押す。


次に、SwiftからObjective-Cのライブラリを呼び出すために必要なBridgeファイルを作成する。
作成するBridgeファイルの名前は下記の通りとする。(各自プロジェクトに置き換えて作成してください。)
gmailTutorial-Bridging-Header.h
File → New → Fileで、下記の画面が出てくる。

先ほどのブリッジファイル名を入力して作成する。


ファイルの中身は下記の通り。

#ifndef gmailTutorial_Bridging_Header_h
#define gmailTutorial_Bridging_Header_h

#import <GTMOAuth2ViewControllerTouch.h> ← これを追記
#import <GTLGmail.h> ← これを追記

#endif /* gmailTutorial_Bridging_Header_h */

続いて、プロジェクトナビゲーターの「gmailTutorial(アイコンが青色のやつ)」をクリックし、真ん中の画面に出てきたBuild Settingsのメニューを選択 Objective-C Bridging Headerの項目にgmailTutorial-Bridging-Header.hファイルのパスを記載する。

$(SRCROOT)/$(PROJECT)/$(SWIFT_MODULE_NAME)-Bridging-Header.h

こんな感じで記載すると、確定したタイミングで実際のパスに書き換えてくれる。
階層構造が異なっている場合は、不要な部分を削除してください。

続いて、チュートリアルに書かれているSwiftのコードをコピペして使えれば良かったのだが、Xcode7でiOS9に対応させようとするとエラーが出る箇所があるので、下記ソースをコピペして下さい。

import UIKit

class ViewController: UIViewController {
    
    private let kKeychainItemName = "Gmail API"
    private let kClientID = "YOUR_CLIENT_ID_HERE"
    private let kClientSecret = "YOUR_CLIENT_SECRET_HERE"
    
    private let scopes = [kGTLAuthScopeGmailReadonly]
    
    private let service = GTLServiceGmail()
    let output = UITextView()
    
    // When the view loads, create necessary subviews
    // and initialize the Gmail API service
    override func viewDidLoad() {
        super.viewDidLoad()
        
        output.frame = view.bounds
        output.editable = false
        output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
        //output.autoresizingMask = UIViewAutoresizing.FlexibleHeight |
        //    UIViewAutoresizing.FlexibleWidth
        output.autoresizingMask = UIViewAutoresizing([.FlexibleHeight, .FlexibleWidth])
        
        view.addSubview(output);
        
        GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
            kKeychainItemName,
            clientID: kClientID,
            clientSecret: kClientSecret
        )
        
    }
    
    // When the view appears, ensure that the Gmail API service is authorized
    // and perform API calls
    override func viewDidAppear(animated: Bool) {
        if let authorizer = service.authorizer,
            canAuth = authorizer.canAuthorize where canAuth {
                fetchLabels()
        } else {
            presentViewController(
                createAuthController(),
                animated: true,
                completion: nil
            )
        }
    }
    
    // Construct a query and get a list of upcoming labels from the gmail API
    func fetchLabels() {
        output.text = "Getting labels..."
        
        //let query = GTLQueryGmail.queryForUsersLabelsList()
        let query = GTLQueryGmail.queryForUsersLabelsList() as! GTLQuery
        service.executeQuery(query,
            delegate: self,
            didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
        )
    }
    
    // Display the labels in the UITextView
    func displayResultWithTicket(ticket : GTLServiceTicket,
        finishedWithObject labelsResponse : GTLGmailListLabelsResponse,
        error : NSError?) {
            
            if let error = error {
                showAlert("Error", message: error.localizedDescription)
                return
            }
            
            var labelString = ""
            
            if !labelsResponse.labels.isEmpty {
                labelString += "Labels:\n"
                for label in labelsResponse.labels as! [GTLGmailLabel] {
                    labelString += "\(label.name)\n"
                }
            } else {
                labelString = "No labels found."
            }
            
            output.text = labelString
            
    }
    
    
    // Creates the auth controller for authorizing access to Gmail API
    private func createAuthController() -> GTMOAuth2ViewControllerTouch {
        //let scopeString = " ".join(scopes)
        let scopeString = scopes.joinWithSeparator(" ")
        return GTMOAuth2ViewControllerTouch(
            scope: scopeString,
            clientID: kClientID,
            clientSecret: kClientSecret,
            keychainItemName: kKeychainItemName,
            delegate: self,
            finishedSelector: "viewController:finishedWithAuth:error:"
        )
    }
    
    // Handle completion of the authorization process, and update the Gmail API
    // with the new credentials.
    func viewController(vc : UIViewController,
        finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
            
            if let error = error {
                service.authorizer = nil
                showAlert("Authentication Error", message: error.localizedDescription)
                return
            }
            
            service.authorizer = authResult
            dismissViewControllerAnimated(true, completion: nil)
    }
    
    // Helper for showing an alert
    func showAlert(title : String, message: String) {
        let alert = UIAlertView(
            title: title,
            message: message,
            delegate: nil,
            cancelButtonTitle: "OK"
        )
        alert.show()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
}


以上でチュートリアルの内容は終了。
YOUR_CLIENT_ID_HEREとYOUR_CLIENT_SECRET_HEREは、各自書き換えて使って下さい。
書き換えた部分はコメントアウトしてあるので、見てもらえればわかるかと思います。
ログインするとGmailのラベルの一覧が表示されるはず。

2015年10月27日火曜日

MailCore2をSwiftプロジェクトで使う

初めてiOSアプリを作るのですが、メール周りの機能が欲しかったので、下記のライブラリを使って実装してみようと思ったのですが、
初めて過ぎて何をどうすればいいのかわからなかったのでメモしておく。
とりあえず今回の記事ではSwiftからObjective-Cを使える様にする所までやってみる。 使ったのはXcode7。

まず、Xcodeで新しいプロジェクトを作成しておく。
今回はSingleViewApplicationで作る。

適当なプロジェクト名をつける

保存場所を指定して作成ボタンをクリック

で、作成が終了したらXcodeは終了しておく。


続いて使用したかったライブラリをインストールしていく。 使いたかったライブラリはこれ。
■MailCore2
https://github.com/MailCore/mailcore2

CocoaPodsでインストール出来るので、ターミナルを起動させて、CocoaPodsをインストールする。
$ sudo gem install cocoapods
Password: ← 自分のMacのログインパスワードを入力
Fetching: nap-1.0.0.gem (100%)
Successfully installed nap-1.0.0
Fetching: cocoapods-core-0.39.0.gem (100%)
Successfully installed cocoapods-core-0.39.0
Fetching: claide-0.9.1.gem (100%)
Successfully installed claide-0.9.1
Fetching: xcodeproj-0.28.2.gem (100%)
Successfully installed xcodeproj-0.28.2
Fetching: cocoapods-downloader-0.9.3.gem (100%)
Successfully installed cocoapods-downloader-0.9.3
Fetching: cocoapods-search-0.1.0.gem (100%)
Successfully installed cocoapods-search-0.1.0
Fetching: cocoapods-stats-0.6.2.gem (100%)
Successfully installed cocoapods-stats-0.6.2
Fetching: cocoapods-try-0.5.1.gem (100%)
Successfully installed cocoapods-try-0.5.1
Fetching: cocoapods-trunk-0.6.4.gem (100%)
Successfully installed cocoapods-trunk-0.6.4
Fetching: molinillo-0.4.0.gem (100%)
Successfully installed molinillo-0.4.0
Fetching: cocoapods-0.39.0.gem (100%)
Successfully installed cocoapods-0.39.0
Parsing documentation for nap-1.0.0
Installing ri documentation for nap-1.0.0
Parsing documentation for cocoapods-core-0.39.0
Installing ri documentation for cocoapods-core-0.39.0
Parsing documentation for claide-0.9.1
Installing ri documentation for claide-0.9.1
Parsing documentation for xcodeproj-0.28.2
Installing ri documentation for xcodeproj-0.28.2
Parsing documentation for cocoapods-downloader-0.9.3
Installing ri documentation for cocoapods-downloader-0.9.3
Parsing documentation for cocoapods-search-0.1.0
Installing ri documentation for cocoapods-search-0.1.0
Parsing documentation for cocoapods-stats-0.6.2
Installing ri documentation for cocoapods-stats-0.6.2
Parsing documentation for cocoapods-try-0.5.1
Installing ri documentation for cocoapods-try-0.5.1
Parsing documentation for cocoapods-trunk-0.6.4
Installing ri documentation for cocoapods-trunk-0.6.4
Parsing documentation for molinillo-0.4.0
Installing ri documentation for molinillo-0.4.0
Parsing documentation for cocoapods-0.39.0
Installing ri documentation for cocoapods-0.39.0
11 gems installed

これでCocoaPodsが使えるようになった。
試しにpodコマンドが使えるのか確認してみる。
$ pod --version
0.39.0

きちんとバージョンが表示されたので、これでCocoaPodsのインストール作業は完了。
これからmailcore2をCocoaPods経由でインストールしていく。
まずターミナルを起動し、Xcodeで作成したプロジェクトファイル(*.xcodeproj)のおいてあるディレクトリへ移動する
自分の場合は「/Users/user_name/Documents/iPhoneApps/mailCoreTest」というプロダクトを作ったのでそこへ移動した。
$ cd /Users/user_name/Documents/iPhoneApps/mailCoreTest

対象のディレクトリへ移動したらPodfileというファイルを作成し、下記の通り記述して保存する。 PodfileはPHPでいうcomposer.jsonみたいなもの。
$ vi Podfile
platform :ios, “7.0”
pod 'mailcore2-ios', '~> 0.5.1'

で、mailcore2をインストールする。
$ pod install
Updating local specs repositories
Analyzing dependencies
Downloading dependencies
Installing mailcore2-ios (0.5.1)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `mailCoreTest.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 1 total
pod installed.
以上でmailcore2を使う事前準備が完了。
mailCoreTest.xcworkspaceというファイルが出来ているので、これをXcodeで開く。

するとPodsというプロジェクトがmailCoreTestの中に出来ている。

後はSwiftプロジェクトからObjective-Cを呼べる様にXcode側で設定してやる

SwiftからObjective-Cのコードを呼ぶためには、Bridging-Header.hという特殊なヘッダファイルを用意してやる必要がある。
で、追加してみる。(Objective-Cのソースがプロジェクトに追加されると、自動で生成される。)
自分で追加する場合は下記の通り。

XcodeのFile -> New -> Fileを選択。もしくは⌘Nを押す。

Header Fileを選択。

プロジェクト名-Bridging-Headerという名前を付けて保存


作成したヘッダファイルの中に下記を追加して保存。
#import <MailCore/MailCore.h>

続いて、追加したヘッダファイルをXcodeに認識させる。
Build Settings -> Swift Compiler – Code Generation
”Install Objective-C Compatibility Header”が”Yes”
”Objective-C Bridging Header”に、”$(SRCROOT)/$(PROJECT)/$(SWIFT_MODULE_NAME)-Bridging-Header.h”と入力する。(実際にBridging-Header.hファイルがあるパスを指定する。)

以上でSwiftからObjective-Cが呼べる様になる。

2015年10月13日火曜日

ConfluenceをローカルのVMに入れてみた

会社で利用しているConfluenceなのですが、これが結構便利で他の事にも使えないかということで、調査目的でローカルのVMにインストールしてみた。

インストール方法は下記のサイトを参考にインストールしました。 Atlassian Confluence インストール ガイド (Linux OS)
インストール完了後にConfluenceのセットアップウィザードみたいなのがあるのですが、
その辺はこちらでも書いておこうと思います。

インストールしたConfluenceのバージョンは若干古めのやつ(5.6.6)を使っています。(会社で利用しているバージョンと同じバージョンにするため)

まず、事前にAtlassianのアカウントを作成する必要があります。
https://id.atlassian.com/signup
上記サイトにアクセスして、アカウントを作成しておきます。

下記のURLにアクセスして、Confluenceのセットアップ画面にいきます。
http://VMのIP:8090/
※ポート番号はインストール手順の中で変更出来ますので、変更している人は自分で指定したポート番号に読み替えて下さい。

画面右上の日本語をクリックして、表示を日本語に変換


トライアル版の使用を始めるをクリック


ライセンスキーの入力画面で、アカウントを持っており、キーを作成したいをチェック


先ほど作成したAtlassianのログイン情報を入力して、「I agree to ~」にチェックして、「サインインしてライセンスキーを作成」ボタンをクリック
クリック後に非常に時間が掛かった・・・。1時間位は放置しておく位の気持ちで挑んだ方がいいのかもしれない。


Manage users~ボタンをクリック(何故かここからは英語になってしまう・・・)


最初にシステム管理者のアカウントを作成するための情報を入力する。


初期設定完了。「Start using Confluence」をクリックしてウェルカムページへ遷移


以上でとりあえずConfluenceが利用出来る状態になる。 Confluence自体のインストール手順が書かれているサイトの一番下にインストール後の設定という項目があるので、 こちらも合わせてやっておくと良いかもしれません。 色々と試してみたいと思います。

2015年10月12日月曜日

SSL接続の仕組み

iOSアプリ開発していた時に色々と横道に逸れていったらSSL通信の仕組みに関して、ちゃんと理解してない事に気がついたので図を書いてみた。


言葉で流れを説明する前に、SSLは「共通鍵暗号方式」と「公開鍵暗号方式」の両方を使っているということを知っておく必要があります。

共通鍵暗号方式


クライアントとサーバで同じ鍵情報を持っている状態で、それぞれが送信時には暗号化し、受信時には復号化する。
クライアント、サーバ共に暗号化、復号化が可能。

公開鍵暗号方式


サーバ上で生成された秘密鍵と公開鍵のキーペアを使う。
クライアントは公開鍵を取得し、サーバ側に公開鍵を使って暗号化した情報を送る。
サーバ側では受信した情報を秘密鍵を使って復号化する。
共通鍵暗号方式との大きな違いは、公開鍵で暗号化された情報は秘密鍵でしか復号出来ないという点。
クライアントにしろ、サーバにしろ、秘密鍵を持っている方でしか復号できない。

以上の事を踏まえて、SSL通信の流れを説明してみる。

1.ユーザーがSSL化されているページにアクセス(SSL接続要求)
2.サーバ側はその接続要求に対し公開鍵を含んでいる証明書を返却
3.ユーザーは証明書から公開鍵を取り出す
4.SSL接続要求をしたサーバと暗号化通信するための共通鍵を作成
5.作成した共通鍵を公開鍵を使って暗号化して、サーバへ送信
6.サーバは受信した共通鍵を秘密鍵を使って復号化
7.処理の終了通知を送る。
8.ユーザーが入力したユーザー名やパスワードを共通鍵を使って暗号化して送信
9.共通鍵を使って復号化し、入力された情報を取り出す。
10.取り出した情報で各種処理を行う。
11.レスポンスを返却する。

以上がざっくりとした流れです。

ネットでググっていると、この辺の情報はいっぱい出てくるのですが、どれが正しいのか正直わかりません・・・。
この記事も間違っている可能性もあります・・・。
程々に参考にして下さい。

2015年7月24日金曜日

phpファイルをテキストファイルとして返却する

Web上で差分を確認するために、phpファイルを「text/plain」として返却する必要があった。
(phpファイルとして解釈されてしまうとパラメータとか諸々展開されてしまうので、正確な差分が取れない)
ただ、差分を確認するためにいちいちテキストファイルにして比較とかはしたくなかったので、
.htaccessファイルを使って、phpファイルはテキストファイルとして解釈するようにした。
その設定がこちら。
php_flag engine off
AddType text/plain php

2015年7月23日木曜日

vagrant haltやsuspendコマンドが使えない

ansible-playbookを処理途中で「Ctrl + C」した影響か、vagrant haltやsuspendコマンドを実行すると下記の様なエラーが出るようになってしまった。
ちなみに、vagrant statusやvagrant sshは普通に使えました。

$ vagrant halt
An action 'halt' was attempted on the machine 'default',
but another process is already executing an action on the machine.
Vagrant locks each machine for access by only one process at a time.
Please wait until the other Vagrant process finishes modifying this
machine, then try again.

If you believe this message is in error, please check the process
listing for any "ruby" or "vagrant" processes and kill them. Then
try again.

原因はホストマシン側(自分の場合はWindows)で動いているrubyによるものらしい。
vagrantコマンドを実行するとホストマシン上でrubyが実行されるのですが、
コマンドの処理終了のタイミングでrubyのプロセスも切れる様になっている。
しかし、このメッセージが出ている時は、rubyのプロセスが切れずに残り続けている状態になっていた。
対応としては、該当のrubyのプロセスを切ってやればいい。
タスクマネージャーを開くと、ruby.exeというプロセスが残っているはずなので、それを右クリックしてプロセス終了をクリック。
終了したら通常通りvagrant haltやsuspendなどが使えるようになりました。

2015年7月22日水曜日

vagrantのバージョンアップしてみた

同僚の人がansible-playbookがうまくいかないとの事だったので、その人と環境を揃える為にvagrantのバージョンを1.6.5から最新の1.7.3に上げてみた。
vagrant自体のバージョンアップは簡単だけど、ちょっと面倒臭い。
旧バージョンをアンインストールして再起動、新バージョンをインストールして再起動すれば完了。
作業自体は単純ですが、Windowsの再起動が遅すぎてすごく時間が掛かった。
SSDに換装してほしい・・・。

Virtualboxのバージョンアップも行ったがこちらはインストーラーを使ってインストールするだけでバージョンアップされる。
両方のバージョンアップが終わった所で、諸々確認していく。
最終的に確認する内容としては、ansible-playbookを実行して、VM上の環境がきちんと出来上がるかどうかを確認する。

まず、1.6.5の時に作ったVMは不要になったので、一度削除して作りなおす事にした。
Cygwin上から下記のコマンドを実行。
$ vagrant destroy
Vagrant is attempting to interface with the UI in a way that requires
a TTY. Most actions in Vagrant that require a TTY have configuration
switches to disable this requirement. Please do that or run Vagrant
with TTY.

今までは普通に削除出来たのに、vagrantのバージョンを上げた途端に削除出来なくなってしまった・・・。
原因はまだ調べていないが、下記の様にオプションをつけることで削除することは可能。
$ vagrant destroy --force
==> default: Destroying VM and associated drives...

Vagrantfile自体は特に変更する必要がなかったので、vagrant upした。
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos64-100g'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: vm-test
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2231 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2231
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/user_name/VirtualBoxMachines/vm-test

赤い文字の部分が多分バージョンアップ後に新しくデータメッセージで、
安全じゃない鍵があったから、より安全な新しい鍵ペアに置き換えるよというメッセージが出ていた。
今まで使っていた1.6系だと、1ユーザーに付き1個の鍵が生成されていて、複数のVMを作ってもその1つの鍵を使いまわして接続ができたが、1.7系にすると1台に付き1個の鍵が生成される(vagrant up時)。
設定を今まで通りの状態でPlaybookを実行すると、下記の様なエラーが出てしまう。

.ssh/configに書かれている鍵情報が古いバージョンの時の鍵のままになっている場合に出るエラー

$ ansible-playbook setup.yml

PLAY [vm-grp] *************************************************************

GATHERING FACTS ***************************************************************
fatal: [vm] => SSH Error: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
    while connecting to 192.168.33.10:22
It is sometimes useful to re-run the command using -vvvv, which prints SSH debug output to help diagnose the issue.

TASK: [check selinux off] *****************************************************
FATAL: no hosts matched or all hosts have already failed -- aborting


PLAY RECAP ********************************************************************
           to retry, use: --limit @/home/user_name/setup.retry

vm                  : ok=0    changed=0    unreachable=1    failed=0

新しい鍵を使うために、.ssh/configを書き換える

Host vm-test
  HostName 192.168.33.10
  Port 22
  User vagrant
  #IdentityFile C:\Users\user_name\.vagrant.d\insecure_private_key <- コメントアウト
  IdentityFile /home/user_name/VirtualBoxMachine/vm1/.vagrant/machines/default/virtualbox/private_key <- 追記

ファイルのパーミッションで出るエラー

$ ansible-playbook setup.yml

PLAY [vm-grp] *************************************************************

GATHERING FACTS ***************************************************************
fatal: [vm] => SSH Error:     while connecting to 192.168.33.10:22
It is sometimes useful to re-run the command using -vvvv, which prints SSH debug output to help diagnose the issue.

TASK: [check selinux off] *****************************************************
FATAL: no hosts matched or all hosts have already failed -- aborting


PLAY RECAP ********************************************************************
           to retry, use: --limit @/home/user_name/setup.retry

vm                  : ok=0    changed=0    unreachable=1    failed=0

上記の様なパーミッションのエラーになったら確認する場所としては、下記が挙げられる。
※下記のパーミッションになっていることを確認する
~/.ssh -> 700
~/.ssh/config -> 744
.vagrant/machines/default/virtualbox/private_key -> 700

上記の対応をしたらちゃんとansible-playbookが実行できた。

2015年7月7日火曜日

久しぶりのOAuth1.0にハマった

ものすごく久々にOAuth周りを触っていたのですが、色々とハマってしまったのでメモを残す。
以前のメモはあまり役に立ちませんでした・・・。
OAuthのライブラリはGoogleのOAuth.php
http://oauth.googlecode.com/svn/code/php/OAuth.php

今回ハマった内容としてはものすごく単純でした。
既存のソースでCURLOPT_POSTFIELDSに対してリクエストパラメータの配列が配列の状態のまま渡っていたため、
BaseStringとかがすごい事になっていたのですが、そうなっている原因がすぐに分からず修正に時間がかかってしまった・・・。

例)HMAC_SHA1の形式でGETリクエストの場合

<?php
include_once 'oauth.php';

$consumerKey    = 'XXXXXXXXXXXX';
$consumerSecret = 'XXXXXXXXXXXX';
$accessToken    = null;
$method         = 'GET';
$url            = 'http://XXXX/XXX.php';
$request_params = array(
    'a' => 1,
    'b' => 2
);

$OAuthConsumer        = new OAuthConsumer($consumerKey, $consumerSecret);
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();

$OAuthRequest = OAuthRequest::from_consumer_and_token($OAuthConsumer, $accessToken, $method, $url, $request_params);
$OAuthRequest->sign_request($OAuthSignatureMethod , $OAuthConsumer, $accessToken);

list($buf, $OAuthAuthorization) = explode(':', $OAuthRequest->to_header(''), 2);

$OAuthAuthorizationHeader  = array(
    'Authorization:'.$OAuthAuthorization,
);

$ch   = curl_init();

curl_setopt($ch, CURLOPT_URL,            $url.'?'.http_build_query($request_params));
curl_setopt($ch, CURLOPT_HEADER,         true);
curl_setopt($ch, CURLOPT_HTTPGET,        true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,     $OAuthAuthorizationHeader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT,        30);

$res = curl_exec($ch);

例)HMAC_SHA1の形式でPOSTリクエストの場合

<?php
$consumerKey    = 'XXXXXXXXXXXX';
$consumerSecret = 'XXXXXXXXXXXX';
$accessToken    = null;
$method         = 'POST';
$url            = 'http://XXXX/XXX.php';
$request_params = array(
    'a' => 1,
    'b' => 2
);

$OAuthConsumer        = new OAuthConsumer($consumerKey, $consumerSecret);
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();

$OAuthRequest = OAuthRequest::from_consumer_and_token($OAuthConsumer, $accessToken, $method, $url, $request_params);
$OAuthRequest->sign_request($OAuthSignatureMethod , $OAuthConsumer, $accessToken);

list($buf, $OAuthAuthorization) = explode(':', $OAuthRequest->to_header(''), 2);

$OAuthAuthorizationHeader  = array(
    'Authorization:'.$OAuthAuthorization,
    'Content-Type: application/x-www-form-urlencoded',
);

$ch   = curl_init();

curl_setopt($ch, CURLOPT_URL,            $url);
curl_setopt($ch, CURLOPT_HEADER,         true);
curl_setopt($ch, CURLOPT_POST,           true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,     $OAuthAuthorizationHeader);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT,        30);
curl_setopt($ch, CURLOPT_POSTFIELDS,     http_build_query($request_params));

$res = curl_exec($ch);

署名をチェック

<?php
include_once 'oauth.php';

$OAuthConsumerKey    = 'XXXXXXXXXXXX';
$OAuthConsumerSecret = 'XXXXXXXXXXXX';

$request              = OAuthRequest::from_request();
$OAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
$consumer             = new OAuthConsumer($OAuthConsumerKey, $OAuthConsumerSecret);

if ($OAuthSignatureMethod->check_signature($request, $consumer, null, $request->get_parameter('oauth_signature'))) {
        echo 'This signature is OK.';
} else {
        echo 'This signature is NG.';
}

2015年6月30日火曜日

vagrantのbox容量を拡張する方法

Vagrantでboxファイルをダウンロードしてくると基本的に容量が少なくて困ることが多かったので、boxのサイズを拡張してみた。
とは言ったものの、サクッと出来る感じではなく、結構手順が面倒くさいです。
何故かと言うと、Vagrantで使われる仮想ディスクイメージはvmdk形式と言われるもので、一度作成したディスクのサイズを変更することが出来ない。
なので、一度変換可能な形式(vdi)に変換して、拡張してから元のvmdk形式に戻すという手順が必要になる。
また、注意点としてVirtualBoxでスナップショット等を使っていた場合、スナップショットで保存されている内容は一切移行出来ない。
(出来る方法があるのかもしれないが、現時点ではわからなかった)

VirtualBoxをインストールしたディレクトリ内に「VBoxManage.exe」というファイルがあるので、それを使って拡張する。
C:\Program Files\Oracle\VirtualBox\VBoxManage.exe

以下、拡張のための手順。

1.VirtualBoxマネージャーで拡張したいVMを選択し、設定画面を開く

拡張したいVMを選択し、右クリック押して、コンテキストメニューの中の設定をクリック。
もしくは拡張したいVMを選択した状態で、設定ボタンを押す。


2.設定画面で拡張したいvmdkファイルの場所を調べる。

ストレージ → XXXXX.vmdk → 場所にマウスカーソルを合わせる。
そうするとフルパスが出てくるので、それを確認する。場所を右クリックするとコピー出来る。
コピーした値は「C:\Users\UserName\VirtualBox VMs\jenkins-laravel\box-disk1.vmdk」とする。


3.コマンドプロンプトを立ち上げて下記のディレクトリへ移動

cd C:\Program Files\Oracle\VirtualBox\

4.VBoxManage.exeがあるか確認

C:\Program Files\Oracle\VirtualBox> dir VBoxManage.exe

5.仮想HDDのUUIDを調べる

先ほど確認したvmdkファイルのパスと、下記のリストの中のLocationが一致するものを探す。
該当のvmdkファイルのUUIDの項目をコピーする。
C:\Program Files\Oracle\VirtualBox> VBoxManage.exe list hdds

UUID:           6e50fc33-ab05-4ca5-9740-ad708e335e11 ← これをコピーする
Parent UUID:    base
State:          created
Type:           normal (base)
Location:       C:\Users\UserName\VirtualBox VMs\jenkins-laravel\box-disk1.vmdk
Storage format: VMDK
Capacity:       30720 MBytes

UUID:           acd294ea-6844-4121-b66f-85f848bcecec
Parent UUID:    base
State:          created
Type:           normal (base)
Location:       C:\Users\UserName\VirtualBox VMs\jenkins-laravel-2\box-disk1.vmdk
Storage format: VMDK
Capacity:       30720 MBytes

6.vmdk形式からvdi形式にしてコピーする

vmdkファイルと同じ内容の仮想ディスクをvdi形式で作成する。
$ VBoxManage.exe clonehd "C:\Users\UserName\VirtualBox VMs\jenkins-laravel\box-disk1.vmdk" "C:\Users\UserName\VirtualBox VMs\jenkins-laravel\clone.vdi" --format vdi

0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone hard disk created in format 'vdi'. UUID: 152e5d9f-e66a-4212-84d6-dbe05c1f8ceb

7.vdi形式の仮想ディスクのサイズを変更する。100GBにしたい場合は下記の通り。単位はMB

$ VBoxManage.exe modifyhd "C:\Users\UserName\VirtualBox VMs\jenkins-laravel\clone.vdi" --resize 102400
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

8.拡張したvdi形式をvmdk形式にしてコピーする

$ VBoxManage clonehd "C:\Users\UserName\VirtualBox VMs\jenkins-laravel\clone.vdi" "C:\Users\UserName\VirtualBox VMs\jenkins-laravel\box-disk1.vmdk" --format vmdk

※上書きするのが怖い場合は別名にして保存し、VirtualBox上でストレージを変更することで対応出来る。
仮想ハードディスクファイルの選択をクリックして、新しく作ったvmdkファイルを選択することで、過去のファイルを残した状態で拡張出来る。
もし拡張したvmdkファイルに何か不都合があった場合は、元の仮想ハードディスクファイルに戻せば元の状態に戻る。


9.vdiファイルをVBoxManger上の管理から外す

ここに記載しているUUIDはvdi形式のファイルのUUID。
$ VBoxManage closemedium disk 152e5d9f-e66a-4212-84d6-dbe05c1f8ceb

ここまでで、vmdkファイルのディスク拡張作業は完了。
ここからは拡張したディスクを使用できるようにする。


10.拡張したVMにログインして、現状を確認する

使用出来る容量は変わっていない事を確認。
# df -h
Filesystem            Size  Used Avail Use% マウント位置
/dev/mapper/vg_vagrantcentos64-lv_root
                       28G  1.8G   24G   7% /
tmpfs                 499M     0  499M   0% /dev/shm
/dev/sda1             485M   32M  428M   7% /boot
vagrant               459G  358G  102G  78% /vagrant

11.VMWareを立ち上げて状態を確認

ディスク容量自体が増えていることを確認する
# fdisk -l

ディスク /dev/sda: 107.4 GB, 107374182400 バイト
ヘッド 255, セクタ 63, シリンダ 13054
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00054ab8

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sda1   *           1          64      512000   83  Linux
パーティション 1 は、シリンダ境界で終わっていません。
/dev/sda2              64        3917    30944256   8e  Linux LVM

ディスク /dev/mapper/vg_vagrantcentos64-lv_root: 29.6 GB, 29569843200 バイト
ヘッド 255, セクタ 63, シリンダ 3594
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000


ディスク /dev/mapper/vg_vagrantcentos64-lv_swap: 2113 MB, 2113929216 バイト
ヘッド 255, セクタ 63, シリンダ 257
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000

12.パーティションを作成する

先ほど、fdiskコマンドで出てきた「ディスク /dev/sda」と書かれているところの「/dev/sda」の部分を入れる。(青文字の部分)
# fdisk /dev/sda

警告: DOS互換モードは廃止予定です。このモード (コマンド 'c') を止めることを
      強く推奨します。 and change display units to
         sectors (command 'u').

コマンド (m でヘルプ): n
コマンドアクション
   e   拡張
   p   基本パーティション (1-4)
p
パーティション番号 (1-4): 3
最初 シリンダ (3917-13054, 初期値 3917): Enter
初期値 3917 を使います
Last シリンダ, +シリンダ数 or +size{K,M,G} (3917-13054, 初期値 13054): Enter
初期値 13054 を使います

コマンド (m でヘルプ): t
パーティション番号 (1-4): 3
16進数コード (L コマンドでコードリスト表示): 8e ← fdiskコマンドの黄文字のとこの値を入れる
領域のシステムタイプを 3 から 8e (Linux LVM) に変更しました

コマンド (m でヘルプ): wq
パーティションテーブルは変更されました!

ioctl() を呼び出してパーティションテーブルを再読込みします。

警告: パーティションテーブルの再読込みがエラー 16 で失敗しました: デバイスもしくはリソースがビジー状態です。
カーネルはまだ古いテーブルを使っています。新しいテーブルは
次回リブート時か、partprobe(8)またはkpartx(8)を実行した後に
使えるようになるでしょう
ディスクを同期しています。

13.状態を再び確認

/dev/sda3が追加され、そこに空き容量が割り当てられていることを確認する
# fdisk -l

ディスク /dev/sda: 107.4 GB, 107374182400 バイト
ヘッド 255, セクタ 63, シリンダ 13054
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00054ab8

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sda1   *           1          64      512000   83  Linux
パーティション 1 は、シリンダ境界で終わっていません。
/dev/sda2              64        3917    30944256   8e  Linux LVM
/dev/sda3            3917       13054    73398975   8e  Linux LVM

ディスク /dev/mapper/vg_vagrantcentos64-lv_root: 29.6 GB, 29569843200 バイト
ヘッド 255, セクタ 63, シリンダ 3594
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000


ディスク /dev/mapper/vg_vagrantcentos64-lv_swap: 2113 MB, 2113929216 バイト
ヘッド 255, セクタ 63, シリンダ 257
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000

14.リブートもしくはpartprobeを実行する

今回はrebootで反映させる。
# reboot

15.物理ボリュームを作成する

# pvcreate /dev/sda3
Physical volume "/dev/sda3" successfully created

16.既存のボリュームグループ(VG) に新しいパーティションを追加する

# vgextend vg_vagrantcentos64 /dev/sda3
  Volume group "vg_vagrantcentos64" successfully extended

17.既存の論理ボリューム(LV) を追加したパーティションの分を拡張する

既存のLV は lvdisplay で確認。「lv_root」が既存で、LSizeが既存のディスク容量
# lvdisplay -C
  LV      VG                 Attr      LSize  Pool Origin Data%  Move Log Cpy%Sync Convert
  lv_root vg_vagrantcentos64 -wi-ao--- 27.54g
  lv_swap vg_vagrantcentos64 -wi-ao---  1.97g

18.拡張できるサイズを確認する

# vgdisplay
  --- Volume group ---
  VG Name               vg_vagrantcentos64
  System ID
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  4
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               2
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               99.50 GiB
  PE Size               4.00 MiB
  Total PE              25473
  Alloc PE / Size       7554 / 29.51 GiB
  Free  PE / Size       17919 / 70.00 GiB
  VG UUID               f4eZwV-24wK-49T7-oERF-da3e-cwoW-gPtAdu

19.指定したサイズにディスクを拡張する

[PE Size] * [Free PE] の値が拡張できるサイズとなるので、4MB * 17919 = 71676MiB を拡張する。
# lvextend -L +71676MiB /dev/vg_vagrantcentos64/lv_root
  Extending logical volume lv_root to 97.54 GiB
  Logical volume lv_root successfully resized

20.ファイルシステムを拡張する

ディスク容量が大容量になると時間が掛かる。
# resize2fs /dev/vg_vagrantcentos64/lv_root
resize2fs 1.41.12 (17-May-2010)
Filesystem at /dev/vg_vagrantcentos64/lv_root is mounted on /; on-line resizing required
old desc_blocks = 2, new_desc_blocks = 7
Performing an on-line resize of /dev/vg_vagrantcentos64/lv_root to 25568256 (4k) blocks.
The filesystem on /dev/vg_vagrantcentos64/lv_root is now 25568256 blocks long.
※CentOS7では「resize2fs」は利用出来ないので、「xfs_growfs」を使う。
# xfs_growfs /dev/vg_vagrantcentos64/lv_root

21.容量が増えていることを確認する

# df -h
Filesystem            Size  Used Avail Use% マウント位置
/dev/mapper/vg_vagrantcentos64-lv_root
                       97G  1.8G   90G   2% /
tmpfs                 499M     0  499M   0% /dev/shm
/dev/sda1             485M   32M  428M   7% /boot

composer update時にgithubのrate limitに引っかかる

composer updateコマンドを行ってライブラリをインストールしていたのですが、途中で下記の様なメッセージが出てきた。

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing XXXXXXX/XXXXXX (X.X.XX
    Downloading: 100%)

  - Installing XXXXXXX/XXXXXX (X.X.XX
    Downloading: 100%)

  - Installing XXXXXXX/XXXXXX (X.X.XX
    Downloading: 100%)

  - Installing XXXXXXX/XXXXXX (X.X.XX
    Downloading: 100%)

  - Installing XXXXXXX/XXXXXX (X.X.XX
    Downloading: Connecting...
Could not fetch https://api.github.com/repos/~~~~~, please create a GitHub OAuth token to go over the API rate limit
Head to https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+host_name+2015-06-30+0254
to retrieve a token. It will be stored in "/home/user_name/.composer/auth.json" for future use by Composer.
Token (hidden):

調べてみると、githubのダウンロード回数制限に引っかかったみたいです。
時間が経てば解消されるのですが、待たずに規制緩和してもらうことも出来るので、その方法で対応してみた。

1.githubにログインして、下記URLにアクセス
https://github.com/settings/tokens

2.右カラムの「Personal access tokens」の中の「Generate new token」ボタンをクリック

3.「Token description」に適当な名前を入れて、「repo」だけを選択。
 「Generate token」ボタンをクリック

4.自分が付けたdescriptionの所に新しいトークンがあるので、それをコピーし、先ほどのメッセージが出た所へペーストする。

以上で対応出来るはず。

2015年6月5日金曜日

Google Cloud SDKとGoogle App Engine SDKにハマった

Google App Engineを使ってみようかと思い、SDKをダウンロードしてきたのですが、
Google Cloud SDKでも使えると思ってダウンロードして来てインストールしてみた。
しかし、手元のWindowsマシンではうまく動かなかった・・・。
その時に諸々調べたので、比べてみた。

■ダウンロード先URL
- Google Cloud SDK
https://cloud.google.com/sdk/

- Google App Engine SDK
https://cloud.google.com/appengine/downloads

■ダウンロードファイル
- Google Cloud SDK
GoogleCloudSDKInstaller.exe

- Google App Engine SDK
GoogleAppEngine-1.9.21.msi

落としてきたファイル自体は全然違うのですが、Google Cloud SDKをインストールしてもGoogle App Engineは使用できる。
インストール先のディレクトリを見ると同じ様な内容がインストールされていた。

■ディレクトリ構成 - Google Cloud SDK
C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine


- Google App Engine SDK
C:\Program Files (x86)\Google\google_appengine


比べてみるとGoogle App Engine SDKの方がファイルが少ない。
また、同じプロジェクトを実行してみても、吐かれるログも若干異なる。

- Google Cloud SDK
2015-06-05 18:31:24 Running command: "['C:\\Python27\\pythonw.exe', 'C:\\Program Files\\Google\\Cloud SDK\\google-cloud-sdk\\platform\\google_appengine\\dev_appserver.py', '--skip_sdk_update_check=yes', '--port=8080', '--admin_port=8000', 'C:\\GoogleAppEngine\\helloworld\\engineapp']"
INFO     2015-06-05 18:31:28,542 devappserver2.py:745] Skipping SDK update check.
INFO     2015-06-05 18:31:28,795 api_server.py:190] Starting API server at: http://localhost:61721
INFO     2015-06-05 18:31:28,819 dispatcher.py:192] Starting module "default" running at: http://localhost:8080
INFO     2015-06-05 18:31:28,822 admin_server.py:118] Starting admin server at: http://localhost:8000


- Google App Engine SDK
2015-06-05 18:06:43 Running command: "['C:\\Python27\\pythonw.exe', 'C:\\Program Files (x86)\\Google\\google_appengine\\dev_appserver.py', '--skip_sdk_update_check=yes', '--port=8080', '--admin_port=8000', 'C:\\GoogleAppEngine\\helloworld\\engineapp']"
INFO     2015-06-05 18:06:43,177 devappserver2.py:745] Skipping SDK update check.
INFO     2015-06-05 18:06:43,970 api_server.py:190] Starting API server at: http://localhost:61256
INFO     2015-06-05 18:06:43,996 dispatcher.py:192] Starting module "default" running at: http://localhost:8080
INFO     2015-06-05 18:06:44,013 admin_server.py:118] Starting admin server at: http://localhost:8000


ちなみに、Google Cloud SDKの方は、ファイル内を修正しないと動かなかった。
dockerが問題になるらしく、docker-pyとかインストールしてみたのですが、解決しなかったので、
とりあえずコメントアウトして対応しました。

参考URL:http://qiita.com/MiCHiLU/items/495e4c6da7a3e7f3925c

2015-06-05 18:27:00 Running command: "['C:\\Python27\\pythonw.exe', 'C:\\Program Files\\Google\\Cloud SDK\\google-cloud-sdk\\platform\\google_appengine\\dev_appserver.py', '--skip_sdk_update_check=yes', '--port=8080', '--admin_port=8000', 'C:\\GoogleAppEngine\\helloworld\\engineapp']"
Traceback (most recent call last):
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\dev_appserver.py", line 83, in 
    _run_file(__file__, globals())
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\dev_appserver.py", line 79, in _run_file
    execfile(_PATHS.script_file(script_name), globals_)
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\devappserver2.py", line 36, in 
    from google.appengine.tools.devappserver2 import dispatcher
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\dispatcher.py", line 29, in 
    from google.appengine.tools.devappserver2 import module
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\module.py", line 74, in 
    from google.appengine.tools.devappserver2 import vm_runtime_factory
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\vm_runtime_factory.py", line 25, in 
    from google.appengine.tools.devappserver2 import vm_runtime_proxy
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\vm_runtime_proxy.py", line 29, in 
    from google.appengine.tools.devappserver2 import log_manager
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\devappserver2\log_manager.py", line 34, in 
    from google.appengine.tools.docker import containers
  File "C:\Program Files\Google\Cloud SDK\google-cloud-sdk\platform\google_appengine\google\appengine\tools\docker\containers.py", line 48, in 
    from docker import docker
ImportError: cannot import name docker
2015-06-05 18:27:00 (Process exited with code 1)

どちらを起動させてもGoogle App Engine Launcherの見た目は同じですが、
初期状態ではGoogle Cloud SDKの方はエラーが出て立ち上がらず、Google App Engine SDKの方は普通に立ち上がります。
※左がGoogle App Engine SDK版、右がGoogle Cloud SDK版です。

cygwinでvi互換モードをやめる

cygwinのvimで矢印キーがABCDになってしまうので、それをやめる方法
.vimrcに
set nocompatible
と記載するだけ。
あとはcygwinを再起動するか、vimrcを再読み込みすれば適用される。
再読み込みの仕方は下記の通り。
:source ~/.vimrc

2015年6月4日木曜日

WindowsのIntelliJで既存のPHPプロジェクトを開く

WindowsでIntelliJを使うようになったのですが、既存のPHPプロジェクトを開こうとした時に、Import Projectを使うと、何故かプロジェクト内のソースをActionScriptだと判断されてしまい、大抵のソースが読み込まれないという自体に。

なので、Create New Projectで既存のディレクトリを指定して開くようにしてみたらうまくいったので、その時のメモ。


1.Create New Projectをクリック


2.PHP Empty Projectを選択して、Nextをクリック


3.Project locationの部分を選択して、読み込みたいディレクトリを選択し、任意のProject nameを入れるてFinishボタンをクリック。このように既存ディレクトリを選択することで、Import Projectと同じ事ができる。

jenkinsを起動時に指定できるオプション一覧

以前の記事にも書いたのですが、Jenkinsをyum経由でインストールするのではなく、warファイルのみをダウンロードして起動しています。
調べてみたらyum経由でインストールした場合にできるファイルがなく、どうすればいいのかわからなかったのですが、どうやら起動時のオプションで指定する事ができるみたいです。
それ以外のオプションも書いてあったので、起動コマンドと一緒にメモしておく。

su -l vagrant -c java -jar /usr/lib/jenkins/jenkins.war --httpPort=18080

その他のパラメータは以下のURLを参考に。
https://wiki.jenkins-ci.org/display/JA/Starting+and+Accessing+Jenkins
コマンドライン・パラメータ説明
--javaHome=$JAVA_HOMEJavaやAntが必要なビルドを実行するときに使用する$JAVA_HOMEをJenkinsに渡します。これはJenkinsを起動するJREではありません(Jenkins上で使用するJREを指定します)。デフォルトは、Jenkinsを実行するのに使用したJREです。
--httpPort=$HTTP_PORTJenkinsが使用するHTTPプロトコルのポート番号を指定します。デフォルトは8080です。これを使用しない(_HTTPS_を使用)ようにするには、-1を指定します。
--httpListenAddress=$HTTP_HOSTこのパラメータで、Jenkinsがリクエストを受け付けるIPアドレスを指定できます。環境変数$HTTP_HOSTが指定されていれば、それを利用します。デフォルトでは 0.0.0.0 です。--- この場合、すべての有効なインタフェース上で(httpPortで指定された)ポートをリスンします。
--httpsPort=$HTTP_PORTHTTPSプロトコルで使用するポート番号を指定します。
--httpsListenAddress=$HTTPS_HOSTJenkinsがHTTPSプロトコルでリクエストを受け付けるIPアドレスを指定できます。環境変数$HTTPS_HOSTが指定されていれば、それを利用します。
--ajp13Port=$AJP_PORT標準のAJP13プロトコルを使う場合、$AJP_PORTRを利用して、Jenkinsがリスンするポートを指定できます。デフォルトは8009です。(_HTTPS_を使用するため) これを使用しないようにするには、-1を指定します。
--ajp13ListenAddress=$AJP_HOSTJenkinsがAJP13プロトコルのリクエストを受け付ける、IPアドレスを指定できます。デフォルトでは 0.0.0.0 です。 --- i.e. この場合、すべての有効なインタフェース上で(ajpPortで指定された) ポートをリスンします。
--argumentsRealm.passwd.$ADMIN_USER=passwordユーザー $ADMIN_USERのパスワードを設定します。Jenkinsのセキュリティが有効化されている場合、Jenkinsやプロジェクトを設定するために、$ADMIN_USERでログインする必要があります。注意: 管理者権限を持つユーザーも指定しなければなりません。(以下を参照してください)
--argumentsRealm.roles.$ADMIN_USER=admin$ADMIN_USERを管理者として設定し、Jenkinsのセキュリティが有効化されていれば、Jenkinsを設定できるようにします。詳しくはJenkinsのセキュリティを参照してください。

2015年5月27日水曜日

vagrantからVirtualBoxが起動出来なくなる問題の解決方法

Windows + Cygwin + Vagrant + VirtualBoxという環境で開発をしているのですが、
たまに、VirtualBoxを立ち上げていない状態で、Cygwinから「vagrant resume」してしまう事がある。
そうすると下記の様なメッセージが出てきて、VMが立ち上がらない状態になってしまう。(人によってはVirtualBoxが立ち上がってくれる?)

$ vagrant resume
==> default: VM not created. Moving on...

VirtualBoxを後から起動させてみると、VirtualBox上はsuspend状態のまま。
しかしvagrantコマンドでステータスを確認してみると

$ vagrant status
Current machine states:

default                   not created (virtualbox)

The environment has not yet been created. Run `vagrant up` to
create the environment. If a machine is not created, only the
default provider will be shown. So if a provider is not listed,
then the machine is not created for that environment.

VMは作成されていない状態になっている。
ちなみにこの状態で「vagrant up」してもすでに同名のVirtualBox名(Vagrantfile内のvb.name)が存在しているためエラーになる。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos6.4'...
==> default: Matching MAC address for NAT networking...
A VirtualBox machine with the name 'centos' already exists.
Please use another name or delete the machine with the existing
name, and try again.

なぜこんな状態になっているかというと、vagrantとVirtualBoxの関連付けをしているファイルが無い、もしくは中身が空の状態になっている事が原因らしい。
下記のファイルがあるか、中身が書かれているかを確認してみる。(Vagrantfileが置いてある所がvagrant_home)

/path/to/vagrant_home/.vagrant/machines/default/virtualbox/id

試しに確認してみたら、

$ ls -la .vagrant/machines/default/virtualbox/
合計 4
drwxr-xr-x+ 1 kusagaya-naoki Domain Users 0 5月  26 15:31 .
drwxr-xr-x+ 1 kusagaya-naoki Domain Users 0 5月  18 13:11 ..

案の定ファイルは存在していなかった。
なので、ここにファイルを作成してやればいいのですが、VirtualBox側のVMのUUIDを調べる必要がある。
UUIDの調べ方は以下の通り。

VirtualBoxのVMのUUIDの調べ方


1.コマンドプロンプトを立ち上げて、VirtualBoxのインストールディレクトリへ移動

cd C:\Program Files\Oracle\VirtualBox

2.VBoxManageでVirtualBoxにあるVMのUUIDの一覧を確認する。
※vm_nameにはvb.nameの名前が書かれている

C:\Program Files\Oracle\VirtualBox> VBoxManage.exe list vms
"vm_name" {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}

これでVMのUUIDがわかったので、下記のディレクトリ配下にidファイルを作成する。

$ vi /path/to/vagrant_home/.vagrant/machines/default/virtualbox/id

ファイルの先頭に調べたUUIDを書いて保存する。

XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

vagrant側のステータスを確認してみる。

$ vagrant status
Current machine states:

default                   saved (virtualbox)

To resume this VM, simply run `vagrant up`.

not createdからsavedに変わった事を確認して起動してみる。

$ vagrant resume
==> default: Resuming suspended VM...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2223
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection refused. Retrying...
==> default: Machine booted and ready!

ちゃんと起動しました。
よかった・・・。

2015年5月11日月曜日

Ansibleで変数入りのファイルをリモートマシンへコピーする

Ansibleで開発環境を作成しようとすると、各種設定ファイル等を使い回したくなります。
通常であれば、ホストマシン側に設定ファイルを置いておき、それをリモートマシンへcopyモジュールを利用してコピーするのですが、それだとホストマシンに置いてある設定ファイルをそのまま設置することになってしまいます。
プロジェクトによって書き換えたい部分とかが出てくると思います。ログの吐き出し先とか。
そういった場合はcopyモジュールを使用するのではなく、templateモジュールを使用する事で、元ファイルに入れてある変数を展開した状態でリモートマシンにコピーする事が出来ます。
変数の使い方に関しては割愛します。

変数入りのファイルを用意します。
$ vi files/file.conf
{{ project_name }}

プレイブックに下記の様な行を追加します。
$ vi playbook.yml
- template: src=files/file.conf dest=/etc/file.conf mode=0644

プレイブックを実行します。
ansible-playbook playbook.yml --extra-vars "project_name=test-project"

処理終了後にリモートマシンにコピーしたファイルを確認します。
$ vi /etc/file.conf
test-project

この様な感じで変数展開された状態のファイルが設置されています。

2015年4月9日木曜日

Vagrantでsuspendからresumeする際のポート問題

vagrantでマシンを起動や停止を行う際に、「vagrant up」「vagrant halt」を使っていたが、
これだと起動や停止にかかる時間が長いのでよく利用するVMに関しては「vagrant suspend」(一時停止)「vagrant resume」(復帰)を利用するようにしている。
しかしこの「vagrant suspend」「vagrant resume」を複数のVMで併用していると、ある工程で作業をすると問題が出てくる。

問題となる手順は以下の通り。
1.VM1を「vagrant up」で起動
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/XXXXXXXXX/XXXXXXXXX/XXXXXXXXX-1
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.

2.VM1を「vagrant suspend」で一時停止
$ vagrant suspend
==> default: Saving VM state and suspending execution...

3.VM2を「vagrant up」で起動
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/XXXXXXXXX/XXXXXXXXX/XXXXXXXXX-2
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.

4.VM2を「vagrant suspend」で一時停止
$ vagrant suspend
==> default: Saving VM state and suspending execution...

5.VM1で「vagrant resume」で起動
$ vagrant resume
==> default: Resuming suspended VM...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection refused. Retrying...
==> default: Machine booted and ready!

6.VM2で「vagrant resume」をするが起動しない。
$ vagrant resume
Vagrant cannot forward the specified ports on this VM, since they
would collide with some other application that is already listening
on these ports. The forwarded port to 2222 is already in use
on the host machine.

To fix this, modify your current projects Vagrantfile to use another
port. Example, where '1234' would be replaced by a unique host port:

  config.vm.network :forwarded_port, guest: 22, host: 1234

Sometimes, Vagrant will attempt to auto-correct this for you. In this
case, Vagrant was unable to. This is usually because the guest machine
is in a state which doesn't allow modifying port forwarding.

ここで、何が問題になって起動しないかというと、ホストマシンからVMにSSH接続する際に使用するポートの重複が問題となっている。
※VMを2台同時に立ち上げる事が問題なのではなく、立ち上げる時の工程に問題がある。

こういった問題を回避する為に、予めローカルからVMにSSH接続する際に使用するポート番号を変更しておく必要がある。
ポート番号の変更方法は、Vagrantfileに下記の様な設定を追加する事で回避出来る。

config.vm.network "forwarded_port", id: "ssh", guest: 22, host: 2223

一度全てのVMを「vagrant halt」で停止させてから、Vagrantfileを編集し、上記設定を追記した状態で「vagrant up」する。
追加する場所は下記のコメントがあるので、その下辺りに追加する。

# config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", id: "ssh", guest: 22, host: 2223 ← この部分を追記

保存した状態で再び「vagrant up」すると、
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2223 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2223
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/XXXXXXXXX/XXXXXXXXX/XXXXXXXXX-2
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.

この様な感じで立ち上がる。
ちなみに複数台立ち上げて「vagrant resume」「vagrant suspend」しても問題ない手順は以下の通り。

1.VM1を「vagrant up」で起動
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/kusagaya-naoki/VirtualBoxMachines/jenkins-laravel
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.

2.VM2を「vagrant up」で起動
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2200 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => C:/cygwin64/home/kusagaya-naoki/VirtualBoxMachines/jenkins-laravel-2
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: to force provisioning. Provisioners marked to run always will still run.

3.VM1を「vagrant suspend」で停止
$ vagrant suspend
==> default: Saving VM state and suspending execution...

4.VM2を「vagrant suspend」で停止
$ vagrant suspend
==> default: Saving VM state and suspending execution...

5.VM1を「vagrant resume」で起動
$ vagrant resume
==> default: Resuming suspended VM...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection refused. Retrying...
==> default: Machine booted and ready!

6.VM2を「vagrant resume」で起動
$ vagrant resume
==> default: Resuming suspended VM...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection refused. Retrying...
==> default: Machine booted and ready!

赤文字にした部分も見てもらえれば一目瞭然だと思いますが、
なぜこの手順で問題ないかというと、「vagrant up」時にはSSHポート番号が重複していた場合に
vagrantが自動で別の開いているポートを使用して立ち上げる様になっている。
しかし、このポートチェックは現在起動しているVMで使用されているポート番号だけで、一時停止されているVMに使われているポート番号はチェック対象にならない。
そのため、手順によってはこういった現象が発生する。
常にこの様な手順で起動させるようにしていればこういった事態にはならないが、常にこの手順を守れるわけではないので、予めポート番号は設定しておいたほうが良さげ。

スッキリわかるJava 第2版を買ってみた。

会社のメンバーと何か作ろうという話になったので、とりあえずJavaの勉強から初めて見る。
どんな本がいいかなと色々探したけど、評価的にはこれが一番良さそうだったので買ってみた。


Kindle版買うかどうか悩みましたが、結局紙の本を買った。
でも電車の中で読んだりするにはKindle版の方が良かったかなと。

これからどんどん読み進めて行こうと思う。

2015年4月7日火曜日

AnsibleでJenkinsのインストールからJenkinsのプラグインインストールまで。

ローカルのVMにjenkinsをインストールして使おうと思ったのですが、調べてみたらyumでインストールしている記事ばかりだったので、 warファイルをダウンロードしてきて、それを起動するシェルスクリプトを作り、サービスとして登録し、起動させるという方法でやってみた。
最終的な成果物としては下記の様なplaybookとなった。
playbookで書かれているjenkins.shも書いておく。

files/jenkins.shの中身
#!/bin/sh
# chkconfig: 2345 70 30
# description: jenkins
# processname: jenkins

java -jar /usr/lib/jenkins/jenkins.war &

ここからAnsibleのプレイブック
---
- hosts: "target_host"
  sudo: yes
  remote_user: vagrant
  tasks:
    - name: JavaのOpenJDKをインストール
      yum: name=java-1.8.0-openjdk state=latest

    - name: JavaのOpenJDK develをインストール
      yum: name=java-1.8.0-openjdk-devel state=latest

    - name: Jenkinsのwarファイルの設置用ディレクトリを作成
      file: dest=/usr/lib/jenkins state=directory

    - name: jenkins.warファイルをサイトからダウンロード
      get_url: url=http://mirrors.jenkins-ci.org/war/latest/jenkins.war
               dest=/usr/lib/jenkins

    - name: ホストPCに置いておいたjenkins.shをリモートPCへコピー
      template: src=files/jenkins.sh
                dest=/etc/init.d/jenkins
                mode=0777

    - name: chkconfigにjenkinsを登録
      action: command chkconfig --add jenkins

    - name: jenkinsの起動レベルを変更
      action: command chkconfig --level 2345 jenkins on

    - name: jenkinsを起動
      service: name=jenkins state=started enabled=yes
      sudo: no

    - name: jenkinsのcliツールをダウンロード
      get_url: url=http://localhost:8080/jnlpJars/jenkins-cli.jar
               dest=/tmp

    - name: プラグインをインストール [build-pipeline-plugin]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin build-pipeline-plugin

    - name: プラグインをインストール [timestamper]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin timestamper

    - name: プラグインをインストール [git]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin git

    - name: プラグインをインストール [jobConfigHistory]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin jobConfigHistory

    - name: プラグインをインストール [cron_column]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin cron_column

    - name: プラグインをインストール [next-executions]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin next-executions

    - name: プラグインをインストール [ssh]
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin ssh

    - name: jenkins restart
      action: command java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 safe-restart


jenkinsのプラグインをコマンドラインからインストールする場合に、
java -jar /tmp/jenkins-cli.jar -s http://localhost:8080 install-plugin XXXXXX
と記載する必要があるのですが、このXXXXXの部分はPluginIDを入れる必要がある。
PluginIDの調べ方はJenkinsのプラグインの一覧があるページから入れたいプラグインをクリックすると、上のほうにある表の左上に「Plugin ID」の項目があるので、そこに書かれているIDを入れる。

Vagrant upをした時のエラー

vagrantを使ってVM作ろうと思ったのですが、Vagrantfileの中身を色々と間違えていた事に気がついたので、1から作リ直そうとした時に出たエラー。

行った作業としては以下の通り。

$ vagrant init box-name

$ vi Vagrantfile

$ vagrant up

ここで間違いに気がついたので、VirtualBoxでマシンを停止。(ここでVirtualBoxで止めるのではなく、vagrant haltしてれば起きなかったかもしれない)

$ vagrant destroy

$ vim Vagrantfile
Vagrantfileの中身を修正(但しvb.nameは同じ)

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'centos6.4-30gb'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: jenkins-laravel
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["modifyvm", "a36f7bef-e56e-4686-91a4-a649a92f3a17", "--name", "jenkins-laravel"]

Stderr: VBoxManage.exe: error: Could not rename the directory 'C:\Users\username\VirtualBox VMs\vagrant-centos64_1_1427941735336_99768' to 'C:\Users\username\VirtualBox VMs\jenkins-laravel' to save the settings file (VERR_ALREADY_EXISTS)
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component SessionMachine, interface IMachine, callee IUnknown
VBoxManage.exe: error: Context: "SaveSettings()" at line 2716 of file VBoxManageModifyVM.cpp

こんな感じのエラーが出た。
色々と試行錯誤してみたけど、原因はvagrant destroyした時に
C:\Users\username\VirtualBox VMs\jenkins-laravel
のフォルダが消えていなかったため、重複していてリネームできなかった事が原因だった。

2015年2月13日金曜日

Ansibleで作った環境をServerspecでテストする

ServerspecとはAnsibleやChef、Puppetといったプロビジョニングツールで作成された環境が、本当に意図した通りに作成されているのかをテストするためのツール。
Rubyで書かれていて、RubyのテストフレームワークであるRSpecの書き方に準拠している。

説明はこのくらいにして、実際に使ってみる。
テストする環境としては、自分のローカルPC上に作成したVMマシンで、
既にVagrant、VirtualBox、Ansibleが使える状態になっている。
今回、ServerspecでテストするサーバはVagrant使って新しいVMを作っておく。

まずはインストールから。
gemがインストールしてあることが前提。

user_name@computer_name ~
$ gem install serverspec
DL is deprecated, please use Fiddle
Fetching: rspec-support-3.2.1.gem (100%)
Successfully installed rspec-support-3.2.1
Fetching: rspec-mocks-3.2.0.gem (100%)
Successfully installed rspec-mocks-3.2.0
Fetching: rspec-expectations-3.2.0.gem (100%)
Successfully installed rspec-expectations-3.2.0
Fetching: rspec-core-3.2.0.gem (100%)
Successfully installed rspec-core-3.2.0
Fetching: rspec-3.2.0.gem (100%)
Successfully installed rspec-3.2.0
Fetching: rspec-its-1.1.0.gem (100%)
Successfully installed rspec-its-1.1.0
Fetching: multi_json-1.10.1.gem (100%)
Successfully installed multi_json-1.10.1
Fetching: net-scp-1.2.1.gem (100%)
Successfully installed net-scp-1.2.1
Fetching: specinfra-2.12.7.gem (100%)
Successfully installed specinfra-2.12.7
Fetching: serverspec-2.8.2.gem (100%)
Successfully installed serverspec-2.8.2
Parsing documentation for multi_json-1.10.1
Installing ri documentation for multi_json-1.10.1
Parsing documentation for net-scp-1.2.1
Installing ri documentation for net-scp-1.2.1
Parsing documentation for rspec-3.2.0
Installing ri documentation for rspec-3.2.0
Parsing documentation for rspec-core-3.2.0
Installing ri documentation for rspec-core-3.2.0
Parsing documentation for rspec-expectations-3.2.0
Installing ri documentation for rspec-expectations-3.2.0
Parsing documentation for rspec-its-1.1.0
Installing ri documentation for rspec-its-1.1.0
Parsing documentation for rspec-mocks-3.2.0
Installing ri documentation for rspec-mocks-3.2.0
Parsing documentation for rspec-support-3.2.1
Installing ri documentation for rspec-support-3.2.1
Parsing documentation for serverspec-2.8.2
Installing ri documentation for serverspec-2.8.2
Parsing documentation for specinfra-2.12.7
Installing ri documentation for specinfra-2.12.7
Done installing documentation for multi_json, net-scp, rspec, rspec-core, rspec-expectations, rspec-its, rspec-mocks, rspec-support, serverspec, specinfra after 40 seconds
10 gems installed

次にテストしたいVMのVagrantfileがあるディレクトリへ移動。
自分の場合は下記のディレクトリだったので、そこへ移動。

user_name@computer_name ~
$ cd VirtualBoxMachines/test

でserverspecを初期化する。

user_name@computer_name ~/VirtualBoxMachines/test
$ serverspec-init
DL is deprecated, please use Fiddle
Select OS type:

  1) UN*X
  2) Windows

Select number: 1 ← serverspecでチェックするマシンがUnix系なのかWindows系なのかを選択

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1 ← SSHで接続するリモートサーバなのか、ローカルサーバなのか。

Vagrant instance y/n: y ← Vagrantで作成したマシンなのか
Auto-configure Vagrant from Vagrantfile? y/n: y ← Vagrantfileを元にしてserverspecの設定ファイルを勝手に作ってくれる
 + spec/
 + spec/default/
 + spec/default/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

初期化すると、上記に書かれているファイルができている。
特に修正するわけでもなく、早速テストしてみる。

user_name@computer_name ~/VirtualBoxMachines/test
$ rake spec
rake aborted!
Circular dependency detected: TOP => spec => spec:all => spec:default => spec:all

Tasks: TOP => spec => spec:all => spec:default
(See full trace by running task with --trace)

エラーになった・・・。
原因をググって見ると、下記の様な記事が出てきたので、とりあえず記事通りに修正してみる。
https://saintaardvarkthecarpeted.com/blog/archive/2014/10/_Circular_dependency_detected__error_in_ServerSpec.html

user_name@computer_name ~/VirtualBoxMachines/test
$ vim Rakefile
require 'rake'
require 'rspec/core/rake_task'

task :spec    => 'spec:all'
task :default => :spec

namespace :spec do
  targets = []
  Dir.glob('./spec/*').each do |dir|
    next unless File.directory?(dir)
    targets << File.basename(dir)
  end

  task :all     => targets
  # task :default => :all ← ここをコメントアウトする

  targets.each do |target|
    desc "Run serverspec tests to #{target}"
    RSpec::Core::RakeTask.new(target.to_sym) do |t|
      ENV['TARGET_HOST'] = target
      t.pattern = "spec/#{target}/*_spec.rb"
    end
  end
end

もう一回試しに走らせてみる。
user_name@computer_name ~/VirtualBoxMachines/test
$ rake spec
/usr/local/bin/ruby.exe -I'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.1/lib':'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/lib' '/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/exe/rspec' --pattern 'spec/default/*_spec.rb'
DL is deprecated, please use Fiddle

Package "httpd"
  should be installed (FAILED - 1)

Service "httpd"
  should be enabled (FAILED - 2)
  should be running (FAILED - 3)

Port "80"
  should be listening (FAILED - 4)

Failures:

  1) Package "httpd" should be installed
     On host `default'
     Failure/Error: it { should be_installed }
       expected Package "httpd" to be installed
       sudo -p 'Password: ' /bin/sh -c rpm\ -q\ httpd
       package httpd is not installed

     # ./spec/default/sample_spec.rb:4:in `block (2 levels) in '

  2) Service "httpd" should be enabled
     On host `default'
     Failure/Error: it { should be_enabled }
       expected Service "httpd" to be enabled
       sudo -p 'Password: ' /bin/sh -c chkconfig\ --list\ httpd\ \|\ grep\ 3:on

     # ./spec/default/sample_spec.rb:8:in `block (2 levels) in '

  3) Service "httpd" should be running
     On host `default'
     Failure/Error: it { should be_running }
       expected Service "httpd" to be running
       sudo -p 'Password: ' /bin/sh -c ps\ aux\ \|\ grep\ -w\ --\ httpd\ \|\ grep\ -qv\ grep

     # ./spec/default/sample_spec.rb:9:in `block (2 levels) in '

  4) Port "80" should be listening
     On host `default'
     Failure/Error: it { should be_listening }
       expected Port "80" to be listening
       sudo -p 'Password: ' /bin/sh -c netstat\ -tunl\ \|\ grep\ --\ :80\\\

     # ./spec/default/sample_spec.rb:13:in `block (2 levels) in '

Finished in 0.33207 seconds (files took 19.95 seconds to load)
4 examples, 4 failures

Failed examples:

rspec ./spec/default/sample_spec.rb:4 # Package "httpd" should be installed
rspec ./spec/default/sample_spec.rb:8 # Service "httpd" should be enabled
rspec ./spec/default/sample_spec.rb:9 # Service "httpd" should be running
rspec ./spec/default/sample_spec.rb:13 # Port "80" should be listening

/usr/local/bin/ruby.exe -I'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.1/lib':'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/lib' '/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/exe/rspec' --pattern 'spec/default/*_spec.rb' failed

テストは通ってない(apacheインストールしてないので当然ですが)けど、動きました。
真っ赤っ赤で不安になる。
ちなみにテストが通る様になった時の結果がこれ。

user_name@computer_name ~/VirtualBoxMachines/test
$ rake spec
/usr/local/bin/ruby.exe -I'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-support-3.2.1/lib':'/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/lib' '/usr/local/lib/ruby/gems/2.1.0/gems/rspec-core-3.2.0/exe/rspec' --pattern 'spec/default/*_spec.rb'
DL is deprecated, please use Fiddle

Package "httpd"
  should be installed

Service "httpd"
  should be enabled
  should be running

Port "80"
  should be listening

Finished in 0.1375 seconds (files took 15.45 seconds to load)
4 examples, 0 failures

緑色になって安心。
こうなるまでひたすらAnsibleのplaybookを修正し続ける。