STNSの設定をGitHub Actionsで自動ビルド&デプロイして優勝した

この記事は[LinuxClub Advent Calendar 2020](https://qiita.com/advent-calendar/2020/linuxclub)の12日目です．

今回は研究室の環境においてID管理サーバであるSTNSの設定を自動生成した取り組みを紹介します．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103672/81f177da-8b27-8974-08a4-3668d1e0e9df.png)


# STNSとは

STNSの正式名称はSimple TOML Name Serviceです．
RESTful APIを実装したサーバによるユーザ管理を実現しています．
詳細は以下の公式サイトに書いてあります．

[STNS: Simple TOML Name Service](https://stns.jp/)

# STNSの設定ファイル

STNSではユーザやグループの情報はTOML形式で設定ファイルに記述します．

```toml:stns.conf
port = 1104

[users.koyama]
id = 2001
group_id = 2001
keys = ["ssh-rsa ZZZZZZZ"]

[groups.operators]
id = 2001
users = ["koyama"]
```

この設定ファイルはユーザ数が増えるにつれ，行数も増加することが考えられました．そこで設定ファイルをプログラムにより動的に生成することにしました．

# STNSの設定ファイルを生成

設定ファイルの生成はPythonで作成したプログラムを使用しています．

```python:gen.py
import toml
import csv


def main():
    my_groups = {}
    result = {}
    with open("groups.csv") as csvfile:
        spamreader = csv.reader(csvfile, delimiter=',', quotechar='"', skipinitialspace=True)
        next(spamreader)
        for row in spamreader:
            groupname, gid = row
            my_groups[groupname] = {"id": int(gid)}

    with open("users.csv") as csvfile:
        spamreader = csv.reader(csvfile, delimiter=',', quotechar='"', skipinitialspace=True)
        next(spamreader)
        for row in spamreader:
            user_id, ssh_key = row
            if result.get(user_id):
                result[user_id]["keys"] += [ssh_key]
            else:
                result[user_id] = {"keys": [ssh_key]}
                
    with open("uid.csv") as csvfile:
        spamreader = csv.reader(csvfile, delimiter=',', quotechar='"', skipinitialspace=True)
        next(spamreader)
        for row in spamreader:
            user_id, uid, gid = row
            if result.get(user_id):
                result[user_id]["id"] = int(uid)
                result[user_id]["group_id"] = int(gid)
            else:
                print("error:", user_id)

    # for k,v in result.items():
    #     print(k, v)

    toml.dump({"users": result, "groups": my_groups}, open('stns_generated.toml', mode='w'))

if __name__ == "__main__":
    main()
```

設定内容(ユーザ，グループ)は以下の形式でCSVファイルとして保存します．

```csvs:users.csv
username, pubkey
koyama, "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA..."
```

```csvs:uid.csv
userid,   uid,  gid
koyama,   2001, 2001
```

```csvs:groups.csv
groupname, gid
koyama, 2001
```

gen.pyを実行するとstns_generated.tomlが生成されます．
これをstns.toml内でincludeすることで設定を利用することができます．

```bash
python gen.py
```

# GitHub Actionsで実行

GitHub ActionsはGitHubが提供するCIプラットフォームです．GitHubへのPushやCommitを始めとする操作やスケジュールされた日時を起点にあらかじめ定義したジョブを実行できます．今回は.github/workflows/deploy.yamlとして以下の設定ファイルを作成しました．

```yaml
name: Deploy base-x

# GitHubのmasterブランチへPushがあると起動
on:
  push:
    branches:
      - master

jobs:
  # self-hosted runner(オンプレミス)で動作するGitHub Actions Runnerで設定ファイルの生成と配置を行う
  deploy:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - name: Setup Python
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Generate config
        run: |
          python gen.py
      - name: Copy config
        run: |
          cp cdsl.toml /share/stns/conf.d
  # STNSの再起動を行う
  restart-m:
    runs-on: base-m
    needs: deploy
    steps:
      - name: Restart STNS
        run: |
          sudo systemctl restart stns.service
  # STNSの再起動を行う
  restart-j:
    runs-on: base-j
    needs: deploy
    steps:
      - name: Restart STNS
        run: |
          sudo systemctl restart stns.service
  # Slackへ変更したことを通知
  slack_notification:
    name: Slack Notification
    runs-on: ubuntu-latest
    needs: [restart-m, restart-j]
    steps:
    - uses: actions/checkout@v2
    - name: Slack Notification
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_CHANNEL: lab-env
        SLACK_COLOR: '#3278BD'
        SLACK_ICON: https://github.com/cdsl-research.png?size=48
        SLACK_MESSAGE: 'STNS Config Update :rocket:'
        SLACK_TITLE: Message
        SLACK_USERNAME: STNS-Config-Updater
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
```

この設定では，設定ファイルのビルドと配置を行いSTNSを再起動しています．その後，変更が完了した通知をSlackにポストします．以下は実際に実行した結果です．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103672/1de8cff2-7d70-d580-2e20-d3313585c219.png)

# おわりに

GitHub Actionsを使うことで設定ファイルのバージョン管理と自動生成，自動デプロイを実現しました．これにより設定ファイルを誰がいつ変更したのか明確に残すことができます．また，コマンドによるサーバのオペレーションを排除することで属人化を防ぐことにつながりました．

今後はSTNSをコンテナ化して自動デプロイするCIをGitHub Actionsを通じて構築したいと思います．