inotifywait でファイルを監視して通知までしたい

# Overview

`inotifywait` に関してはこのあたりが詳しいのでこちらを参照。
https://qiita.com/hana_shin/items/9e03ad7a40b4fd7afb67

inotifywait でファイルを監視するまでは調べるとよく出てくるけど、
モニターの結果を通知するいい感じの方法が見付からなかったので、試した。

# 準備

`docker` + `centos:7` で試します。

```yaml:docker-compose.yml
version: '3'

services:
  os:
    image: centos:7
    tty: true
```

コンテナが即終了しないように `tty: true` を付けてる。

## 起動

```bash
docker-compose up
```

## コンテナに入る

centos7が立ち上がってるので、bashで入る。

```bash
docker-compose exec os bash
```

## パッケージなど

```bash
yum update -y
yum install -y epel-release
yum install -y inotify-tools
yum install -y jq nc
```

`jq` と `nc` は後で使います。

# 監視してみる

## ファイルを準備

```
[root@92790f6e5f76 /]# pwd
/
[root@92790f6e5f76 /]# mkdir a
[root@92790f6e5f76 /]# echo test > /a/test.txt
```

## 実行

`--format` 指定がややこしいことになってますが、 `\t` がタブ文字に展開されないので愚直に繋いでます。
理由は後述。

```bash:シェルA
inotifywait -m --format "%T"$'\t'"%w"$'\t'"%f"$'\t'"%e" --timefmt '%F %T' /a
```

## 変更してみる

別のシェルで入って

```bash:シェルB
touch /a/test.txt
```

とかやると、

```bash:シェルA
[root@92790f6e5f76 /]# inotifywait -m --format "%T"$'\t'"%w"$'\t'"%f"$'\t'"%e" --timefmt '%F %T' /a
Setting up watches.
Watches established.
2019-07-09 04:28:20	/a/		OPEN,ISDIR
2019-07-09 04:28:20	/a/		ACCESS,ISDIR
2019-07-09 04:28:20	/a/		CLOSE_NOWRITE,CLOSE,ISDIR
2019-07-09 04:28:21	/a/	test.txt	OPEN
2019-07-09 04:28:21	/a/	test.txt	ATTRIB
2019-07-09 04:28:21	/a/	test.txt	CLOSE_WRITE,CLOSE
```

みたいに出力されます。

`%e` がイベント名のcsvに展開されるため、それ以外の部分をtsvにしたかった感じですね。

## 整形する

1行ずつのtsvにできたので、後々処理しやすいように jsonl に整形します。

```bash:シェルA
inotifywait -m --format "%T"$'\t'"%w"$'\t'"%f"$'\t'"%e" --timefmt '%F %T' /a \
  | jq -R -c '{hoge: .|split("\t")}'
```

```bash:シェルB
touch /a/test.txt
```

出力はこんな感じ。

```json:シェルA
{"hoge":["2019-07-09 04:35:10","/a/","test.txt","OPEN"]}
{"hoge":["2019-07-09 04:35:10","/a/","test.txt","ATTRIB"]}
{"hoge":["2019-07-09 04:35:10","/a/","test.txt","CLOSE_WRITE,CLOSE"]}
```

## １行ずつ処理する

が、このままだとまるごと１本のストリームなので、`while read` で１行ずつ処理するように修正します。

```bash:シェルA
inotifywait -m --format "%T"$'\t'"%w"$'\t'"%f"$'\t'"%e" --timefmt '%F %T' /a \
  | while read x; do
    echo "$x" | jq -Rc '{hoge: .|split("\t")}';
  done
```

```bash:シェルB
touch /a/test.txt
```

見た目は同じ。

```bash:シェルA
{"hoge":["2019-07-09 04:38:33","/a/","test.txt","OPEN"]}
{"hoge":["2019-07-09 04:38:33","/a/","test.txt","ATTRIB"]}
{"hoge":["2019-07-09 04:38:33","/a/","test.txt","CLOSE_WRITE,CLOSE"]}
```

# 通知

さて、これで１行ずつ json にして処理できるようになったので、とりあえず curl で投げてみます。

nc で簡易httpサーバを立てて、

```bash:シェルC
while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "OK"; } | nc -l 8080; done
```

監視を開始。

```bash:シェルA
inotifywait -m --format "%T"$'\t'"%w"$'\t'"%f"$'\t'"%e" --timefmt '%F %T' /a \
  | while read x; do
    echo "$x" | jq -Rc '{hoge: .|split("\t")}' | curl localhost:8080 -H 'Content-Type: application/json' -d @-;
  done
```

変更すると、

```bash:シェルB
touch /a/test.txt
```

```bash:シェルC

POST / HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:8080
Accept: */*
Content-Type: application/json
Content-Length: 56

{"hoge":["2019-07-09 04:44:18","/a/","test.txt","OPEN"]}POST / HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:8080
Accept: */*
Content-Type: application/json
Content-Length: 58

{"hoge":["2019-07-09 04:44:18","/a/","test.txt","ATTRIB"]}POST / HTTP/1.1
User-Agent: curl/7.29.0
Host: localhost:8080
Accept: */*
Content-Type: application/json
Content-Length: 69

{"hoge":["2019-07-09 04:44:18","/a/","test.txt","CLOSE_WRITE,CLOSE"]}
```

監視の結果を http で投げられました。

# フィルタとか

* `inotifywait` 自体にもイベントのフィルタあるし、
* `jq` でフィルタしてもいいし、

そのあたりはお好みで。

ここまで書いて気付いたけど、 ファイルに落として `fluentd` でもいけますねこれ。
っていうかそっちの方が良いと思う（今更

おわり。