2016/5/13 JAWS-UG アーキテクチャ専門支部 ハイブリッドクラウド分科会 CDP議論会 #5 の、発表枠番外編で紹介したもの。
lsyncd は、デフォルトでは rsync を使ってファイルのコピーを行いますが、じつは中で Lua の処理理系が動いていて、設定ファイルは Lua のスクリプトになっています。ということは、inotify で通知を受け取ったら実行したい処理を Lua で書けば、好きなことができます。いくつかサンプルも用意されています。
というわけで、書いてみたのがこんなファイル。
AWS CLI をつかって aws s3 cp
を実行します。3回トライしてダメだったら aws sns publish
で通知します。
Lua から外部コマンドとして、aws s3 cp
を呼んで終了コードを参照してリトライ…なんてことができればよいのですが、単純に aws s3 cp
を呼ぶと、終了コードが非0
で返って来ると lsyncd ごと終了してしまうので、シェルのワンライナーで処理しています。
Lua の文と、文字列として埋め込まれたシェルのワンライナーが混じってわかりにくいですが、local runcmd =
がワンライナーを組み立てている部分です。
ワンライナーの最後の || :
は終了コードを上書きするためのコメント文です。
source_dir = "/home/lsync/upload"
s3bucket = "my-s3-bucket"
prefix = "my-prefix"
sns_topic_arn = "SNS_TOPIC_ARN"
msg_subject = "SNS_MSG_SUBJECT"
snscmd = "aws sns publish --topic-arn '" .. sns_topic_arn .. "' --subject '" .. msg_subject .. "'"
settings {
logfile = "/home/lsync/lsyncd.log",
statusFile = "/home/lsync/lsyncd.status",
nodaemon = false,
statusInterval = 1,
delay = 5,
}
cp = function(event)
local src_path = event.sourcePathname
local dst_path = event.targetPathname
if (string.sub(event.source, -1, -1) == "/") then
src_path = string.sub(event.source, 1, -2) .. event.pathname
end
if (string.sub(event.target, -1, -1) == "/") then
dst_path = string.sub(event.target, 1, -2) .. event.pathname
end
local s3cmd = "aws s3 cp '" .. src_path .. "' '" .. dst_path .. "'"
local msg_body = "command failed: " .. s3cmd
local msg = " --message '" .. string.gsub(msg_body, "'", "\"") .. "'"
local runcmd = "rc=0 && [ -f '" .. src_path .. "' ] && for try in 1 2 3; do " .. s3cmd .. "; rc=$?; [ $rc -eq 0 ] && break; done || " .. snscmd .. msg .. " || :"
spawnShell(event, runcmd)
end
rm = function(event)
local src_path = event.sourcePathname
local dst_path = event.targetPathname
if (string.sub(event.source, -1, -1) == "/") then
src_path = string.sub(event.source, 1, -2) .. event.pathname
end
if (string.sub(event.target, -1, -1) == "/") then
dst_path = string.sub(event.target, 1, -2) .. event.pathname
end
local s3cmd = "aws s3 rm '" .. dst_path .. "'"
local msg_body = "command failed: " .. s3cmd
local msg = " --message '" .. string.gsub(msg_body, "'", "\"") .. "'"
local runcmd = "rc=0 && [ ! -f '" .. src_path .. "' ] && for try in 1 2 3; do " .. s3cmd .. "; rc=$?; [ $rc -eq 0 ] && break; done || " .. snscmd .. msg .. " || :"
spawnShell(event, runcmd)
end
mv = function(event)
local src_path = event.o.targetPathname
local dst_path = event.d.targetPathname
if (string.sub(event.o.target, -1, -1) == "/") then
src_path = string.sub(event.o.target, 1, -2) .. event.o.pathname
end
if (string.sub(event.d.target, -1, -1) == "/") then
dst_path = string.sub(event.d.target, 1, -2) .. event.d.pathname
end
local s3cmd = "aws s3 mv '" .. src_path .. "' '" .. dst_path .. "'"
local msg_body = "command failed: " .. s3cmd
local msg = " --message '" .. string.gsub(msg_body, "'", "\"") .. "'"
local runcmd = "rc=0 && [ -f '" .. src_path .. "' ] && for try in 1 2 3; do " .. s3cmd .. "; rc=$?; [ $rc -eq 0 ] && break; done || " .. snscmd .. msg .." || :"
spawnShell(event, runcmd)
end
s3sync = {
maxProcesses = 1,
onCreate = cp,
onModify = cp,
onDelete = rm,
-- onMove = mv,
}
sync {
s3sync,
source = source_dir,
target = "s3://" .. s3bucket .. "/" .. prefix,
}