即フォーマットチェックにincronを使う

こちらは<a href="https://qiita.com/advent-calendar/2020/nifty">ニフティグループアドベントカレンダー</a>の24日目記事です。 :christmas_tree: 

# はじめに
今回はディレクトリやファイルの変更を監視してそのイベントが発生したら指定したコマンドが実行できる**incron**を紹介します。
使うことになった背景、incronについて、簡単な実装サンプルを書きましたのでご参考ください。


# 背景
ファイル連携しているあるシステムの現状です。

- 連携先
    - 連携するファイルの作成は専用ツールで作成できるが、稀に手動で作成する場合もある。
    - 作成したファイルのフォーマットチェックができるシステムを持っていない。
    - WinSCPを使って連携元のサーバ（linux)にファイルを手動でアップロードしている。
- 連携元
    - アップロードされたファイルはバッチで定期的に取得し、フォーマットチェックを行ってから取り込んでいる。

## 問題点
- フォーマットに問題があった場合、フォーマットチェックを行っている**連携元がエラーの原因を把握してリカバリーを行う**必要がある。→　時間がかかる。
- 連携先はファイルをアップロードする時にエラーにならないか不安になる。

## 実現したいこと
ということで **linuxのサーバにファイルがアップロードされたら即検知し、フォーマットチェックができるようにしたい**、またその結果をSlackに通知することで連携先がエラー内容に気づき、すぐファイルの修正ができるようにしたかったです。


# incronについて
ファイルアップロードの検知に使ったのは **incron** というものです。
Linuxカーネル2.6.13以降で使えて他のOSでは使えません。
ディレクトリやファイルの作成・修正・日付変更などのイベントをトリガーにして指定したコマンドが実行できます。

## incronの使い方
CentOS 7を基準にします。yumを使ってインストールできます。
### incronのインストール
```bash
$ sudo yum install incron
// デーモンの実行
$ sudo systemctl start incrond
```
### incronの設定
使い方はcronと似ています。

```bash
// incronの設定
$ sudo incrontab -e

// incronの反映
$ sudo incrontab -d

// incron設定の確認
$ sudo incrontab -l
```

以下の例は `/home/test/test.txt` に何らかの修正が入ったら `/home/test/start.sh` を実行するという設定です。

```bash
<監視したいディレクトリやファイル> <トリガー> <実行コマンド>
/home/test/test.txt IN_MODIFY sh /home/test/start.sh
```


### incronのパラメータについて
**トリガー**
トリガーは複数つなげて使うことができます。

|トリガー    |説明|
|:-----------|:----------------------|
|IN_ACCESS   |ファイルにアクセスされた（読み取り）|
|IN_ATTRIB|Metadata changed (権限、タイムスタンプ、拡張子など)|
|IN_CLOSE_WRITE|書き込み可能ファイルが閉じられた|
|IN_CLOSE_NOWRITE|書き込み不可能ファイルが閉じられた|
|IN_CREATE|監視ディレクトリにファイル/ディレクトリが作成された|
|IN_DELETE|監視ディレクトリからファイル/ディレクトリが削除された|
|IN_DELETE_SELF|監視対象のファイル/ディレクトリ自体が削除された|
|IN_MODIFY|ファイルが修正された|
|IN_MOVE_SELF|監視対象のファイル/ディレクトリ自体が移動された|
|IN_MOVED_FROM|ファイルが監視対象ディレクトリから移動された|
|IN_MOVED_TO|ファイルが監視対象ディレクトリに移動された|
|IN_OPEN|ファイルが開かれた|

**実行コマンド**
実行コマンドには以下のワイルドカードをつなげて使えます。

|ワイルドカード|説明|
|:-----------|:-----------------------|
|$$| $ をプリント|
|$@|  監視しているパス|
|$#|   イベントに関連されたファイル名|
|$%| イベントのフラグ (文字)|
|$&|  イベントのフラグ (数字)|


### 使い方の例
説明だけでは分かりにくいので実際に使ってみます。
`/home/test/`の配下にファイルが `IN_CREATE`（作成）されたら `ワイルドカードたちを標準出力・ログに書き込み`します。

```
// 設定の確認
$ incrontab -l
/home/test/     IN_CREATE       echo "$$ $@ $# $% $&" > /home/log/test.log

// ファイルを作成してみる
$ touch /home/test/test.txt 

// このように書かれました。
$ cat /home/log/test.log
$ /home/test/ test.txt IN_CREATE 256
```
ワイルドカードの中ではパスとファイル名を取得できる　`$@` `$#` がよく使えるのではないかと思います。



# 簡単サンプル(シェル)
`/home/test ` ディレクトリ配下に作成されたファイルの名前が正しいかをチェックして結果をSlackに送るスクリプトです。
ここでの正しいファイル名とは`test_今日の日付`です。

```bash:incron
$ sudo incrontab -l
/home/test      IN_CREATE       sh /home/test/file_name_check.sh $@ $# > /home/log/test.log
```
```bash:file_name_check.sh
#!/bin/bash

file_dir=${1}
file_name=${2}
valid_file_name=test_$(date "+%Y%m%d")

CHANNEL="[チャンネル名]"
TITLE="ファイルフォーマットチェック"
SLACK_WEB_HOOK='SlackのIncoming Webhook'

# ファイル名をチェック
if [ $file_name != ${valid_file_name} ]; then
  message="ファイル名が正しくありません。"
  status="danger"
else
  message="フォーマットに問題ありませんでした。"
  status="good"
fi

# slackに送信するのデータ
payload="payload={
    \"channel\": \"${CHANNEL}\",
    \"attachments\": [{
    \"title\": \"${TITLE}\",
    \"text\": \"ファイル名: ${file_name} \n 検証結果詳細: \n ${message}\",
    \"color\": \"${status}\"
    }],
}"

# slack送信
curl -s -S -X POST --data-urlencode "${payload}" ${SLACK_WEB_HOOK}
```
トリガーでシェルスクリプト(file_name_check.sh)を実行するようにしていますが、もちろん**PHPやPythonなど別の言語のファイルも呼び出せます**。

## 動作確認
ファイルを二つOKとNGパターンで作成してみます。

```bash
// 今日の日付のファイル名
$ touch /home/test/test_20201214
// 未来日のファイル名
$ touch /home/test/test_20201225
```
このようにSlack通知されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158927/02279f88-446f-5e70-7f11-aeb241f47476.png)

# 最後に
もちろんAWS S3やLambdaを使うなどもっといけてる方法もありますが、
既存の環境の中で解決させる必要があったのでincronを採用しました。
同じ状況下であれば、incronは**トリガー**と**実行コマンド**の組み合わせで色んなことができると思いますので使ってみてください。

では Merry Christmas Eve! :christmas_tree: :gift: :snowman2: 

# 参考サイト
https://www.linux.com/topic/desktop/how-use-incron-monitor-important-files-and-folders/