mdadm RAID1復旧の試験

下準備

ディスクは3本ある状態。状況確認

fdisk -l

フォーマット

sdb,sdcでraid1作成。sddはスペアディスク。それぞれ10MB。

fdisk /dev/sdb
fdisk /dev/sdc
fdisk /dev/sdd

fdiskのコマンドはこんな。

n, p, 1, 1
t, fd

raid0作成

mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sd[bc]1
mkfs.ext4 /dev/md0
mount /dev/md0 /mnt/

確認

mdadm --misc --detail /dev/md0
mdadm --misc --examine /dev/sdb1
mdadm --misc --examine /dev/sdc1

停止と再開のテスト

umount /mnt
mdadm --misc --stop /dev/md0
mdadm --misc --detail /dev/md0
mdadm --assemble /dev/md0 /dev/sd[bc]1

[root@localhost ~]# mdadm --assemble /dev/md0 /dev/sd[bc]1
mdadm: /dev/md0 has been started with 2 drives.

ケース: sdbのディスクが壊れた

sdbに失敗マークつける

mdadm --manage /dev/md0 --fail /dev/sdb1
mdadm --misc --detail /dev/md0

[root@localhost ~]# mdadm --misc --detail /dev/md0
/dev/md0:
Version : 1.2
Creation Time : Mon Dec 16 11:37:59 2013
Raid Level : raid1
Array Size : 7936 (7.75 MiB 8.13 MB)
Used Dev Size : 7936 (7.75 MiB 8.13 MB)
Raid Devices : 2
Total Devices : 2
Persistence : Superblock is persistent
_
Update Time : Mon Dec 16 11:42:49 2013
State : clean, degraded
Active Devices : 1
Working Devices : 1
Failed Devices : 1
Spare Devices : 0
Name : localhost.localdomain:0 (local to host localhost.localdomain)
UUID : 8de6a2e9:620c76ea:cc0bb7fd:5b0c30af
Events : 18
_
Number Major Minor RaidDevice State
0 0 0 0 removed
1 8 33 1 active sync /dev/sdc1
_
0 8 17 - faulty spare /dev/sdb1

sdbを取り除く

mdadm /dev/md0 --remove /dev/sdb1
mdadm --misc --detail /dev/md0

[root@localhost ~]# mdadm /dev/md0 --remove /dev/sdb1
mdadm: hot removed /dev/sdb1 from /dev/md0
[root@localhost ~]# mdadm --misc --detail /dev/md0
/dev/md0:
Version : 1.2
Creation Time : Mon Dec 16 11:37:59 2013
Raid Level : raid1
Array Size : 7936 (7.75 MiB 8.13 MB)
Used Dev Size : 7936 (7.75 MiB 8.13 MB)
Raid Devices : 2
Total Devices : 1
Persistence : Superblock is persistent
_
Update Time : Mon Dec 16 11:50:11 2013
State : clean, degraded
Active Devices : 1
Working Devices : 1
Failed Devices : 0
Spare Devices : 0
_
Name : localhost.localdomain:0 (local to host localhost.localdomain)
UUID : 8de6a2e9:620c76ea:cc0bb7fd:5b0c30af
Events : 21
_
Number Major Minor RaidDevice State
0 0 0 0 removed
1 8 33 1 active sync /dev/sdc1

この状態でも使えることは使える

再起動してみるとどうなる?

/dev/md127で認識されてる。
これはinitramfsで自動assembleされるため。
dracutとかで作り直せばよさそうだけど、パラメータわからん。

sudo fdisk -l

ディスク /dev/md127: 8 MB, 8126464 バイト
ヘッド 2, セクタ 4, シリンダ 1984
Units = シリンダ数 of 8 * 512 = 4096 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000

デグレーデッドな状態

mdadm --misc --detail /dev/md127

[root@localhost ~]# mdadm --misc --detail /dev/md127
/dev/md127:
Version : 1.2
Creation Time : Mon Dec 16 11:37:59 2013
Raid Level : raid1
Array Size : 7936 (7.75 MiB 8.13 MB)
Used Dev Size : 7936 (7.75 MiB 8.13 MB)
Raid Devices : 2
Total Devices : 1
Persistence : Superblock is persistent
_
Update Time : Mon Dec 16 11:53:23 2013
State : clean, degraded
Active Devices : 1
Working Devices : 1
Failed Devices : 0
Spare Devices : 0
_
Name : localhost.localdomain:0 (local to host localhost.localdomain)
UUID : 8de6a2e9:620c76ea:cc0bb7fd:5b0c30af
Events : 31
_
Number Major Minor RaidDevice State
0 0 0 0 removed
1 8 33 1 active sync /dev/sdc1

mount /dev/md127 /mnt/

マウントすればもちろん使える

新規ディスク追加

mdadm /dev/md127 --add /dev/sdd1
mdadm --misc --detail /dev/md127

addするとrebuildが走る。けど、この場合は10MBのディスクなので一瞬で終わってる。

[root@localhost ~]# mdadm /dev/md127 --add /dev/sdd1
mdadm: added /dev/sdd1
[root@localhost ~]# mdadm --misc --detail /dev/md127
/dev/md127:
Version : 1.2
Creation Time : Mon Dec 16 11:37:59 2013
Raid Level : raid1
Array Size : 7936 (7.75 MiB 8.13 MB)
Used Dev Size : 7936 (7.75 MiB 8.13 MB)
Raid Devices : 2
Total Devices : 2
Persistence : Superblock is persistent
_
Update Time : Mon Dec 16 15:55:14 2013
State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
Spare Devices : 0
_
Name : localhost.localdomain:0 (local to host localhost.localdomain)
UUID : 8de6a2e9:620c76ea:cc0bb7fd:5b0c30af
Events : 56
_
Number Major Minor RaidDevice State
2 8 49 0 active sync /dev/sdd1
1 8 33 1 active sync /dev/sdc1

md127からmd0に戻す

mdadm --misc --stop /dev/md127
mdadm --assemble /dev/md0 /dev/sd[cd]1

[root@localhost ~]# mdadm --misc --stop /dev/md127
mdadm: stopped /dev/md127
[root@localhost ~]# mdadm --assemble /dev/md0 /dev/sd[cd]1
mdadm: /dev/md0 has been started with 2 drives.

ケース:追加したsddのディスクも壊れた

mdadm /dev/md0 --fail /dev/sdd1
mdadm /dev/md0 --remove /dev/sdd1

sdbに新しいディスクを入れて追加する

mdadm /dev/md0 --add /dev/sdb1

おっけー

ケース:片肺で動かす

追加ディスクないけどとりあえず動かしたい、もしくは、片方のディスクだけでデータみたい、というケース。

1ディスクだけでアセンブル

runをつけないとinactiveでの起動になる。

mdadm --assemble /dev/md0 /dev/sdc1 --run

[root@localhost ~]# mdadm --assemble /dev/md0 /dev/sdc1 --run
mdadm: /dev/md0 has been started with 1 drive (out of 2).
[root@localhost ~]# cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 sdc1[1]
7936 blocks super 1.2 [2/1] [_U]
_
unused devices:
[root@localhost ~]# mdadm --detail /dev/md127
mdadm: cannot open /dev/md127: No such file or directory
[root@localhost ~]# mdadm --detail /dev/md0
/dev/md0:
Version : 1.2
Creation Time : Mon Dec 16 11:37:59 2013
Raid Level : raid1
Array Size : 7936 (7.75 MiB 8.13 MB)
Used Dev Size : 7936 (7.75 MiB 8.13 MB)
Raid Devices : 2
Total Devices : 1
Persistence : Superblock is persistent
_
Update Time : Mon Dec 16 16:01:55 2013
State : clean, degraded
Active Devices : 1
Working Devices : 1
Failed Devices : 0
Spare Devices : 0
_
Name : localhost.localdomain:0 (local to host localhost.localdomain)
UUID : 8de6a2e9:620c76ea:cc0bb7fd:5b0c30af
Events : 81
_
Number Major Minor RaidDevice State
0 0 0 0 removed
1 8 33 1 active sync /dev/sdc1

いちおう使えるので、mount -o ro /dev/md0 /mntとかで読み取り専用でマウントするのがよいと思う。

Node.js, Express3, Socket.ioで作る、コマンド実行Webインターフェース

動画をポータブルな形式に変換したり、DVDのバックアップとったり、BitTorrentのファイルダウンロードを登録したり、 ejectコマンドでディスクトレイを開けたり、とか、たまーにサーバー上で簡単なコマンドを実行したいこととかありますよね。

そういうのを一度、Python, Flaskで書いてみた[ https://github.com/shrkw/run-your-command-via-web-ui-py] けど、やっぱりコマンド実行したら標準出力も標準エラーもすぐに見たいよね、と思い、これこそWebSocketの出番や!となったので、Node.js + Socket.ioで書き直してみた。

構成する要素とできたもの

画面はこだわりがなかったので、普通にTwitter bootstrapで。
twbsの静的ファイルも配る必要があるので、Express3も。
要素としてはこれくらいで、できたのはこれ。

https://github.com/shrkw/run-your-command-via-web-ui

screen image

app.jsにちょろっと、Socke.io周りのコードを追加したくらい。
コマンド実行自体は、child_process.spawnで実行すると、stdoutとstderrがコールバックでとれるので、それぞれのコールバックでクライアント側のログ描画イベントをemitするのみ。シンプル。

感想

Socket.io

マジでらくちん。
onで受け口作っといて、emitでイベント送るだけというのがすごくわかりやすい。 難しいことを考えなくていい。

Bower

普通に楽チンなので捗る。でも、Node.js入れないといけないのはアプリケーションを他言語で作ってるときに少し面倒かな。主要言語にポーティングされると嬉しい。

READMEはPythonのプロジェクトだろうが、Markdownで書く。

課題

READMEにも書いたけど、クライアント側JSのio.connectの行でSocket.ioサーバーに繋ぐけど、サーバー名をベタで書かないといけないのが残念。

MySQLでAUTO_INCREMENTを任意の数字に変更する

Redmine のチケットを復活させた ときに、誤ってissuesテーブルに違うtsvを食わせてしまい、ゴミデータを追加してauto_incrementの数字を大きくしてしまった。

手動で直したので、そのときの手順。

手順

状況確認

Auto_increment列をみる。

show table status like 'issues'\G;
mysql> show table status like 'issues'\G;
*************************** 1. row ***************************
           Name: issues
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 4713
 Avg_row_length: 1004
    Data_length: 4734976
Max_data_length: 0
   Index_length: 1376256
      Data_free: 9437184
 Auto_increment: 13008
    Create_time: 2013-09-12 16:06:01
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 row in set (0.01 sec)

ERROR: 
No query specified

昨日まで5000くらいだったのに…

データをバックアップしておく

壊れたデータなのでいらないと思うけど、一応バックアップしておく。

mysql -u redmine -p -D redmine_db -e'select * from issues where id > 5348;' > issues.tsv

AUTO_INCREMENTを変更する

AUTO_INCREMENTで指定した数字より大きいidがなければ、正しく変更されるはず。

ALTER TABLE issues AUTO_INCREMENT = 5348;

変更できてなくても、Query OKが返ってくるので注意。しっかり確認すべし。

mysql> ALTER TABLE issues AUTO_INCREMENT = 5348;
Query OK, 5068 rows affected (0.98 sec)
Records: 5068  Duplicates: 0  Warnings: 0

Redmineの誤って削除したチケットをバックアップから復活させた話

参考

基本的にここの記事の通りです。

http://daily-postit.blogspot.jp/2012/12/blog-post.html

前提

DBもファイルもバックアップを残してあること。

手順

ステップは2つ

  • データの復旧
  • ファイルの復旧

データの復旧

復旧するデータのあるテーブル

issues
journals
journal_details
watchers
attachments
issue_relations
custom_values

追記:custom_values が抜けてた。以下のSQLは追記済。

SQL的にはこんな感じ。

select * from issues where id = 4987;
select * from journals where journalized_id = 4987;
select jd.* from journal_details jd INNER JOIN journals j ON journal_id = j.id where journalized_id = 4987;
select * from watchers where watchable_id = 4987;
select * from attachments where container_id=4987;
select * from issue_relations where issue_from_id=4987 OR issue_to_id=4987;
select * from custom_values where customized_id=4987;

実行したコマンド

こんな感じ。シェルスクリプトにすれば再利用できるんじゃないかな!再利用する場面に遭遇したくないけど!!

Export

mkdir /tmp/201309
cd /tmp/201309
mysql -u root -D redmine_20130911_temp -e'select * from issues where id = 4987;' > issues.tsv
mysql -u root -D redmine_20130911_temp -e'select * from journals where journalized_id = 4987;' > journals.tsv
mysql -u root -D redmine_20130911_temp -e'select jd.* from journal_details jd INNER JOIN journals j ON journal_id = j.id where journalized_id = 4987;' > journal_details.tsv
mysql -u root -D redmine_20130911_temp -e'select * from watchers where watchable_id = 4987;' > watchers.tsv
mysql -u root -D redmine_20130911_temp -e'select * from attachments where container_id=4987;' > attachments.tsv
mysql -u root -D redmine_20130911_temp -e'select * from issue_relations where issue_from_id=4987 OR issue_to_id=4987;' > issue_relations.tsv
mysql -u root -D redmine_20130911_temp -e'select * from custom_values where customized_id=4987;' > custom_values.tsv

Import

/tmp/201309/のファイルからINFILEしているのは、mysqlユーザーからファイルにアクセスできる必要があるため。

mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/issues.tsv' INTO TABLE issues IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/journals.tsv' INTO TABLE journals IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/journal_details.tsv' INTO TABLE journal_details IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/watchers.tsv' INTO TABLE watchers IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/attachments.tsv' INTO TABLE attachments IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/issue_relations.tsv' INTO TABLE issue_relations IGNORE 1 LINES;"
mysql -u root -D redmine_db -e"LOAD DATA INFILE '/tmp/201309/custom_values.tsv' INTO TABLE custom_values IGNORE 1 LINES;"

ファイルの復旧

ファイル名を調べる。

mysql> select disk_directory, disk_filename from attachments where container_id=4987;
+----------------+----------------------------------------------------+
| disk_directory | disk_filename                                      |
+----------------+----------------------------------------------------+
| 2013/08        | 130805171804_01_init.sql                           |
| 2013/08        | 130805203239_d8d397a2800db492580e9f034275dab1.xls  |
| 2013/08        | 130806142525_fc0be408d4ccbf8d79b9617874e85a6e.xlsx |
| 2013/08        | 130809181036_02_create_view.sql                    |
| 2013/08        | 130820112759_mail.sql                              |
| 2013/09        | 130903194816_fc0be408d4ccbf8d79b9617874e85a6e.xlsx |
+----------------+----------------------------------------------------+

あとはバックアップしておいたファイルから、filesディレクトリにコピーすればOK.

sudo cp -p opt/redmine/files/2013/08/130805171804_01_init.sql /opt/redmine/files/2013/08/
sudo cp -p opt/redmine/files/2013/08/130805203239_d8d397a2800db492580e9f034275dab1.xls /opt/redmine/files/2013/08/
sudo cp -p opt/redmine/files/2013/08/130806142525_fc0be408d4ccbf8d79b9617874e85a6e.xlsx /opt/redmine/files/2013/08/
sudo cp -p opt/redmine/files/2013/08/130809181036_02_create_view.sql /opt/redmine/files/2013/08/
sudo cp -p opt/redmine/files/2013/08/130820112759_mail.sql /opt/redmine/files/2013/08/
sudo cp -p opt/redmine/files/2013/08/130903194816_fc0be408d4ccbf8d79b9617874e85a6e.xlsx /opt/redmine/files/2013/08/

復旧後のトラブル

なにが原因か不明だけど、復旧作業の翌日にチケットを新規発行したら、チケットのidが昨日まで5000くらいだったのが、いきないり13000とかに!

思い出した!issuesにLOAD DATA INFILEのクエリー投げるときに、間違えて違うtsvを食わせちゃったんだ。自分が原因でよかった。

issuesテーブルみたら、idが飛び飛びになって、なんか変なデータが20行くらい増えてる。 手動でいらないレコード消してissuesテーブルのauto_increment直して解決させたけど、イリーガルなことしたらDB含めて全体をリスタートするとかしたほうが良さげ。

Shrewsoft VPN clientをMacでQTのGUIなしで動かす話

要約

Shrewsoft VPN clientはLinux/BSD/Mac向けのソースが公開されてるので、自分でコンパイルすればQtなしで動くバイナリが作れる。その場合はQtないから当然CUIなので、ターミナルから使うよ。

背景

無料のVPNクライアントとしてたぶん人気だと思うShrewsoft VPN Client。Macで動かしたいひとはたぶんこの辺りをみて、QtだのTUN/TAP driverだのいろいろ入れて、ようやくdmgからインストールして動かしてると思う。

http://ulaptech.blogspot.jp/2012/11/shrew-soft-vpn-client-for-mac-os-x.html http://blog.livedoor.jp/kerorou/archives/28739240.html

でも、僕はQtとかあまり使わないもの/詳しくないものをインストールしたくなかったのでいろいろ見てたら、Linux/BSD版もあったので、そっちで動かせるんじゃないかと試行錯誤してたら、うまいこと動いてくれたので記録を残しておく。

MacにビルトインのVPNもあるんだからそれを使うのが一番綺麗なんだけど、ビルトインVPNクライアントからの接続方法がわからんかった。。。

ちなみにVPNの対向先はJuniper SSG5。

手順

  1. Cmakeをインストール
  2. ソースをダウンロード & ビルド
  3. ファイアウォールでikedを許可
  4. 接続

cmakeをインストール

ikeはビルドするのにCmakeを使ってるみたいなので、事前にインストールする。手軽にhomebrew使った。

sudo brew install cmake

ソースをダウンロード & ビルド

https://www.shrew.net/download/ike からStableの最新をダウンロード。 展開したら、以下のコマンドを実行していく。インストール先は /usr/local/ にしたけど、

cd Download/ike
cmake -DNATT=YES -DCMAKE_INSTALL_PREFIX=/usr/local/

ファイアウォールでikedを許可

Macのビルトインファイアウォールが有効になっている場合、トンネル接続時のnegotiationがタイムアウトで失敗する。 上記の通りにインストールすると、/usr/local/sbin/ikedが存在すると思うので、これをファイアウォールの許可リストに入れる。

と思ったのだけど、/usr以下はFinderからはそのままでは見えないのね ( ;゚皿゚)ノシΣ フィンギィィーーッ!!! ってなりかけたけど、一時的に移動 -> フォルダへ移動/usrを開いて、サイドバーに追加して事なきを得た。

http://d.hatena.ne.jp/testnoda/20120515/1337069061

問題と解決

問題と解決 1: failed to attach to key daemon

$ ikec -r 'hogehoge.vpn' -a
ii : ## : VPN Connect, ver 2.2.0
## : Copyright 2012 Shrew Soft Inc.
## : press the <h> key for help
>> : config loaded for site 'hogehoge.vpn'
!! : failed to attach to key daemon

ikedを再起動しよう!!!

問題と解決 2: peer config failed

$ ikec -r 'hogehoge.vpn' -a
ii : ## : VPN Connect, ver 2.2.0
## : Copyright 2012 Shrew Soft Inc.
## : press the <h> key for help
>> : config loaded for site 'hogehoge.vpn'
>> : attached to key daemon ...
!! : peer config failed
>> : detached from key daemon

クライアントとデーモンのバージョンが違うとこれがでる。以前にdmgで提供されているパッケージをインストールしていませんか? その場合、/usr/bin/ikecというバイナリが置いてあるのでこいつが邪魔をしてます。上記の通りにインストールしてあれば、正しいikec/usr/local/bin/ikecにあると思うので、/usr/bin/ikecは消しましょう。

どうでもいいけど、/usr/bin/ikecはよくわからんライブラリにリンクが張られているな。なんだろ。

$ otool -L /usr/bin/ikec
/usr/bin/ikec:
    /Users/mgrooms/ike/head/source/libike/ShrewSoftIke.framework/Versions/2.2.0/ShrewSoftIke (compatibility version 2.2.0, current version 2.2.0)
    /Users/mgrooms/ike/head/source/libidb/ShrewSoftIdb.framework/Versions/2.2.0/ShrewSoftIdb (compatibility version 2.2.0, current version 2.2.0)
    /Users/mgrooms/ike/head/source/libith/ShrewSoftIth.framework/Versions/2.2.0/ShrewSoftIth (compatibility version 2.2.0, current version 2.2.0)
    /Users/mgrooms/ike/head/source/liblog/ShrewSoftLog.framework/Versions/2.2.0/ShrewSoftLog (compatibility version 2.2.0, current version 2.2.0)
    /usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 123.0.0)
    /usr/lib/libedit.2.dylib (compatibility version 2.0.0, current version 2.11.0)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)

問題と解決 3: negotiation timout occurred

ii : ## : VPN Connect, ver 2.2.1
## : Copyright 2013 Shrew Soft Inc.
## : press the <h> key for help
>> : config loaded for site 'hogehoge.vpn'
>> : attached to key daemon ...
>> : peer configured
>> : iskamp proposal configured
>> : esp proposal configured
>> : client configured
>> : local id configured
>> : remote id configured
>> : pre-shared key configured
ii : bringing up tunnel ...
!! : negotiation timout occurred
ii : tunnel disabled
>> : detached from key daemon

ファイアウォールが閉じてると思う。上記の手順でikedを許可しよう。 ちなみにこのケースの場合は/var/log/iked.logにこんなログが出てるはず。

13/09/05 12:48:04 DB : phase1 resend event scheduled ( ref count = 2 )
13/09/05 12:48:14 -> : resend 1 phase1 packet(s) [0/2] 192.168.x.x:500 -> w.x.y.z:500
13/09/05 12:48:24 -> : resend 1 phase1 packet(s) [1/2] 192.168.x.x:500 -> w.x.y.z:500
13/09/05 12:48:34 -> : resend 1 phase1 packet(s) [2/2] 192.168.x.x:500 -> w.x.y.z:500
13/09/05 12:48:44 ii : resend limit exceeded for phase1 exchange
13/09/05 12:48:44 ii : phase1 removal before expire time
13/09/05 12:48:44 DB : phase1 deleted ( obj count = 0 )
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : policy not found
13/09/05 12:48:44 DB : removing tunnel config references
13/09/05 12:48:44 DB : removing tunnel phase2 references
13/09/05 12:48:44 DB : removing tunnel phase1 references

それでもうまくいかないときは

log_level debug;

にして、ikedのログをみるのがいいです。

https://www.shrew.net/support/VPN_Bug_Report_Unix

Sails.js の res.view() でのviewファイル指定の仕方の話

Sails.js のviewは、デフォルトではURLから自動で解決して任意のviewファイルを表示してくれる。 けど、自分で指定して任意のファイルを使う場合で少しハマったので記録しておく。

参考ディレクトリ構成

views
├── 404.ejs
├── 500.ejs
├── foo
│   ├── hoge.ejs
│   └── index.ejs
├── error.ejs
├── home
│   └── index.ejs
├── layout.ejs

ケース1

foo/hoge へのリクエストを受けた場合で、デフォルトでは、 foo/hoge.ejs がviewに使用される。 もし、 foo/index.ejs を使用したい場合、

res.view("foo/index");

と指定する。 http://sailsjs.org/#!documentation/views

ケース2

ここで、もし error.ejs を指定したい場合、以下の2つは等価に見えるけど挙動が違う。

res.view("error");
res.view("./error");

後者のほうだとうまく動くけど、前者の場合、以下のようなエラーになる。

verbose: Running res.view(error) method...
verbose: Error finding layout file Error: ENOENT, readdir '/Users/hiroyuki.shirakawa/Development/WORKSPACES/Sails/testProject/views/error/'

ソースコードを読んでみたけど、なんだか七面倒臭そうなことをしており、なんとも処理しずらい。なので、Sails直さないでそのまま使うことにした。

Sails のレイアウトに任意のブロックを追加する話

トップのページだけそのページ用の js とか css とか読ませたいときにどうすればいいのか知りたかったけど、Sails の View のドキュメント http://sailsjs.org/#!documentation/views だと Partial の説明くらいしかなかった。

なので、ちょろっと調べてみたりしたことをまとめる。

Sails はデフォルトのテンプレートは ejs http://embeddedjs.com/ 。で、 ejs は簡単なテンプレート機能 https://code.google.com/p/embeddedjavascript/wiki/Templates しか有していないので、 Sails では ejs-local https://github.com/RandomEtc/ejs-locals というモジュールを追加して、 layout, partial, block という機能を追加している。

layout.ejs
<!DOCTYPE html>
<html>
  <head>
    <title><%- title %></title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <%- scripts %>
    <%- stylesheets %>
   <%- blocks.specific_scripts %>
  </head>
  <body>
    <%- body %>
    <script type="text/javascript" src="/js/socket.io.js"></script>
    <script type="text/javascript" src="/js/sails.io.js"></script>
    <script type="text/javascript" src="/js/app.js"></script>
    </body>
</html>
foo/index.ejs
<% script('foo.js') -%>
<% stylesheet('foo.css') -%>
<% block('specific_scripts', '<script type="text/javascript">alert(123);</script>') -%>

<div id="content">
  <h2>It works!</h2>
</div>

で、レンダリングさせると、こんなHTMLが出力されまする。

<!DOCTYPE html>
<html>
  <head>
    <title>my first Sails</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <script src="foo.js"></script>
    <link rel="stylesheet" href="foo.css" />
   <script type="text/javascript">alert(123);</script>
  </head>
  <body>
    
<div id="content">
  <h2>It works!</h2>
</div>

    <script type="text/javascript" src="/js/socket.io.js"></script>
    <script type="text/javascript" src="/js/sails.io.js"></script>
    <script type="text/javascript" src="/js/app.js"></script>
    </body>
</html>