Kong Gatewayカスタムプラグインをテストする　～Part1: Pongoを実行する～

# はじめに
Kong Gateway では、カスタムプラグインのテスト用に Pongo というツールが用意されています。  
テスト環境が一気に準備できて便利です。

ここでは Pongo を実行し、Pongo 上でカスタムプラグインを作成する方法を紹介します。

最新の情報については[公式サイト](https://docs.konghq.com/gateway/latest/plugin-development/)を参照してください。


# Kong Gatewayカスタムプラグインの開発
### カスタムプラグインとは
Kong Gateway は最初から多くのプラグインが入った状態で提供されます。ほとんどの場合、特に Kong Gateway Enterprise / Kong Konnect を利用する場合は、バンドルされたプラグインとその設定だけでも広範囲に対応できますが、実際に動作しているAPIに対して完全にはフィットしないこともまたしばしばあります。
そのようなときは Kong Gateway では自前のカスタムプラグインを作成して対応することが可能です。

プラグインは Lua で記述しますが、他に Go, Pytyon, JavaScript でも記述できます。

プラグインの作成については例えば以下の記事が参考になると思います。

https://qiita.com/caunu-s/items/b6ea0a86252ad7b3f6dc

ここではひな型として、レスポンスに次のヘッダを追加するプラグインを Lua で記述します。
```
My-Plugin-Header: example
```
### ファイルの配置
プラグインは決められたファイル名で配置する必要があります。プラグインを開発するフォルダを kong-plugin-example、 プラグイン名を plugin-example とした場合、ファイル構成は以下のようになります。

```
kong-plugin-example/
+-- kong/
|   +-- plugins/
|       +-- plugin-example/
|           +-- handler.lua
|           +-- schema.lua
+-- spec/
|   +-- plugin-example/
|       +-- 01-simple_spec.lua
+-- plugin-example-0.0.1-1.rockspec
```
:::note info
最低限必要なのは handler.lua, schema.lua の 2ファイルです。
:::
:::note info
このファイル配置は、Kong Gateway 上のプラグインのファイル配置と同じになっています。
:::

ファイルの中身は以下のようになります。
- handler.lua
プラグインの本体です。

```lua: handler.lua
-- プラグインハンドラのオブジェクトです。
-- ローカル変数なので名称は自由です。
local PluginExampleHandler = {
    -- Kong Gatewayのプラグインが複数使われる場合の実行順です。値が大きいほど先に実行されます。
    PRIORITY = 1000,
    -- プラグインのバージョン番号を major.minor.patch の形式で指定します。
    VERSION = "0.0.1",
}

-- このプラグインでの access ファンクションの動作を記述します。
-- access は Upstream の呼び出し前と応答を受け取ってクライアントに返す直前に呼ばれる、プラグインの主要ファンクションです。
-- パラメータ conf には schema.lua の config フィールドに対して管理APIまたは管理画面から設定した値が入ってきます。
function PluginExampleHandler:access(conf)
    -- ここでは応答ヘッダに My-Plugin-Header: example を追加します。
    kong.response.set_header("My-Plugin-Header", "example")
end

-- モジュールのハンドラオブジェクトを返します。
return PluginExampleHandler


```

:::note
handler.lua の仕様は[こちら](https://docs.konghq.com/gateway/latest/plugin-development/custom-logic/#handlerlua-specifications)です。
:::

https://docs.konghq.com/gateway/latest/plugin-development/custom-logic/#handlerlua-specifications

- schema.lua
プラグイン名 (plugin-example) と、必要な設定をここに記述します。


```lua: schema.lua
-- このプラグインの名称です
-- このソースコードでは1回しか参照しませんが、定義が複雑になるとソースコード上に何度も現れるものなので変数化します。
local PLUGIN_NAME = "plugin-example"

-- プラグインの名称と設定項目をオブジェクト変数化します。
local schema = {
  -- プラグイン名
  name = PLUGIN_NAME,
  -- フィールドを定義します。
  fields = {
    {
      -- 設定項目を定義します。
      -- このプラグインでは Route と Service が指定できれば良いので他に設定項目がなく、空になっています。
      config = {
        type = "record",
        fields = {
        },
      },
    },
  },
}

-- このプラグインのスキーマオブジェクトを返します。
return schema

```
:::note
schema.lua の仕様は[こちら](https://docs.konghq.com/gateway/latest/plugin-development/configuration/#schemalua-specifications)です。
:::

https://docs.konghq.com/gateway/latest/plugin-development/configuration/#schemalua-specifications


- spec/plugin-example/ 下にはテストシナリオを配置します。
シナリオの内容については「[Kong Gatewayカスタムプラグインをテストする　～Part2: Pongoでテストする～](https://qiita.com/jirokichi2017/items/35ccee0e09ce80365920)」に書く予定です。   

- plugin-example-0.0.1-1.rockspec  
Pongo を使ってプラグインをパッケージ化することができます。
パッケージ化についても「[Kong Gatewayカスタムプラグインをテストする　～Part2: Pongoでテストする～](https://qiita.com/jirokichi2017/items/35ccee0e09ce80365920)」に書く予定です。   

:::note
luarocks は Lua のパッケージマネージャです。
:::

https://luarocks.org/
# Pongo の実行

### Pongoとは

Pongo はカスタムプラグインのテストフレームワークを提供します。
Kong Gateway を実行するために必要なツールとテストフレームワークがそろった状態で提供されます。

Pongo についての詳細は以下を参照してください。

https://konghq.com/blog/engineering/custom-lua-plugin-kong-gateway

https://github.com/Kong/kong-pongo

### Pongo のインストール
pongo は Linux, Windows, macOS で動作します。
それぞれの環境でのインストールについては[ドキュメント](https://github.com/Kong/kong-pongo?tab=readme-ov-file#installation)をみていただくとして、ここでは Ubuntu または WSL2 の Ubuntu を前提に説明します。

:::note info
公式ドキュメントでは WSL2 上で Debian の利用を勧めていますが Ubuntu での動作でも特に問題はありませんでした。
:::

#### 必要なソフトウェア
Pongo のインストールには次のツールが必要となります。

- Docker Compose
WSL2 で使用する場合は Docker Desktop for Windows でも大丈夫です。
Ubuntu から使用できるようにしておいてください。

- curl
多分入っていると思いますが念のため。

- git
WSL2 の場合は入っていると思いますが念のため。

:::note
今回使用した各ツールのバージョンは以下の通りです。

- Ubuntu 22.04.5 LTS (Windows 10 上の WSL2)
- Docker-CE 27.3.1 ([Dockerのサイトにある手順](https://docs.docker.com/engine/install/ubuntu/) でインストール)
- Docker Compose 2.29.7 (Docker-CE に含まれる)
- curl 7.81.0 (apt でインストール)
- Git 2.34.1 (apt でインストール)
:::

#### インストール
次のコマンドを実行するとインストールされます。
```shell
curl -Ls https://get.konghq.com/pongo | bash
```
:::note
root 権限は不要です。
:::

```shell-session: 実行例
$ curl -Ls https://get.konghq.com/pongo | bash
 
This script will install the Kong Pongo project to:
  /home/ubuntu/.kong-pongo
 
A symbolic link will be created at ~/.local/bin/pongo
 
If necessary, add the following to your .bashrc or .zshrc file
to put the pongo command on the path:
  export PATH=$PATH:~/.local/bin
 
Continue? (y/n) y
Directory /home/ubuntu/.kong-pongo does not exist. Cloning repository...
Cloning into '/home/ubuntu/.kong-pongo'...
remote: Enumerating objects: 7497, done.
remote: Counting objects: 100% (1552/1552), done.
remote: Compressing objects: 100% (467/467), done.
remote: Total 7497 (delta 1180), reused 1273 (delta 1056), pack-reused 5945 (from 1)
Receiving objects: 100% (7497/7497), 4.57 MiB | 9.48 MiB/s, done.
Resolving deltas: 100% (4371/4371), done.
Updating files: 100% (38538/38538), done.
Pongo installed successfully.
$ 
```
- `Continue? (y/n)` には、y で応答してください。

- 実行画面にあるとおり、`~/.kong-pong`ディレクトリに実体がインストールされ、`~/.local/bin/pongo`にシンボリックリンクが張られます。
- PATH を設定すれば、次回のログイン以降は `pongo` で起動できます。
(`~/.bashrc` または `~/.profile` の最終行に `export PATH=$PATH=~/.local/bin` を追記)
- Pongoのインストールについては[公式の手順](https://docs.konghq.com/gateway/latest/plugin-development/get-started/testing/#install-pongo)も参照してみてください。
- 企業内での実行など http-proxy が必要な場合は次の環境変数をあらかじめ設定しておいてください。
インストールおよび Pongo 実行中に使われる curl 用です。
    - http_proxy: http-proxy の URL
    - https_proxy: https-proxy の URL 
- https の証明書を付け替えることで https のサイトもスキャン対象にしている環境があると思います。  
それが原因で Pongo の実行コンテナをビルドする際エラーになる場合は Pongo 起動前に次の環境変数を設定すると改善することがあります。  
  - PONGO_INSECURE: false 以外の文字列
  :::note warn
  PONGO_INSECURE 環境変数を設定しておくだけでも良いのですが、 false を設定すると https の検証が有効化されてしまうので、明示的に true を設定しておくことをお勧めします。
  :::

# Pongo の実行手順
作成するプラグインを含んだ Kong Gateway を Pongo 上で実行します。

### プラグインプロジェクトの初期化
`pongo init` をプラグインプロジェクト (kong-plugin-example フォルダ下) で実行します。
これでプロジェクトのプラグインが Pongo 上の Kong Gateway に登録されます。

:::note
Pongo 上で動作する Kong Gateway に対してテスト対象のプラグインを手動でインストールする必要はありません。
:::
```shell-session: Pongo によるプラグインプロジェクトの初期化
$ cd kong-plugin-example
$ pongo init
[pongo-INFO] found plugin directory: ./kong/plugins/plugin-example
[pongo-INFO] added '.busted' config file for the Busted test framework
[pongo-INFO] added '.luacov' config file for the LuaCov test coverage tool
[pongo-INFO] added '.editorconfig' config file with editor defaults and style items
[pongo-INFO] added '.luacheckrc' config file for the LuaCheck linter
[pongo-INFO] added '.pongo/pongorc' config file for Pongo test dependencies
[pongo-INFO] added 'servroot' to '.gitignore'
[pongo-INFO] added '*.rock' to '.gitignore'
[pongo-INFO] added '.pongo/.bash_history' to '.gitignore'
[pongo-INFO] added 'luacov.stats.out' to '.gitignore'
[pongo-INFO] added 'luacov.report.out' to '.gitignore'
[pongo-INFO] added '.containerid' to '.gitignore'
$ 
```
### 実行準備
Kong Gateway の実行にはデータストアが必要ですが、 Pongo ではあらかじめ Docker コンテナとしての実行できるようになっています。`pongo init` の後は `pongo up` を実行して、データストアを準備します。

```shell-session: pongo up
$ pongo up
[+] Running 15/15
 ✔ postgres Pulled                                     13.2s 
   ✔ fa1690ae9228 Pull complete                         5.3s 
   ✔ a73f6e07b158 Pull complete                         5.5s 
   ✔ 973a0c44ddba Pull complete                         5.5s 
   ✔ 07e5342b01d4 Pull complete                         5.6s 
   ✔ 578aad0862c9 Pull complete                         5.9s 
   ✔ a0b157088f7a Pull complete                         6.0s 
   ✔ 6c9046f06fc5 Pull complete                         6.0s 
   ✔ ae19407bdc48 Pull complete                         6.1s 
   ✔ e53b7c20aa96 Pull complete                         9.5s 
   ✔ a135edcc0831 Pull complete                         9.5s 
   ✔ fed07b1b1b94 Pull complete                         9.6s 
   ✔ 18d9026fcfbd Pull complete                         9.6s 
   ✔ 4d2d5fae97d9 Pull complete                         9.6s 
   ✔ d419466e642d Pull complete                         9.7s 
[+] Running 2/2
 ✔ Network pongo-03ed406e               Created         0.2s 
 ✔ Container pongo-03ed406e-postgres-1  Started         0.8s 
$ 
```
動作確認には Pongo の CLI (shell) 上で、httpie または curl を使用するのが手軽です。これらは実行コンテナに含まれているからです。
動作確認のために shell を起動し、実行準備をします。
```shell-session: pongo shell
$ pongo shell

                /~\ 
  ______       C oo
  | ___ \      _( ^)
  | |_/ /__  _/__ ~\ __   ___
  |  __/ _ \| '_ \ / _ `|/ _ \
  | | | (_) | | | | (_| | (_) |
  \_|  \___/|_| |_|\__, |\___/
                    __/ |
                   |___/  v2.14.0

Kong version: 3.8.0

Kong auto-reload is enabled for custom-plugins and dbless-configurations. Once you
have started Kong, it will automatically reload to reflect any changes in the files.
Use 'pongo tail' on the host to verify, or do 'export KONG_RELOAD_CHECK_INTERVAL=0' in
this shell to disable it.

Get started quickly with the following aliases/shortcuts:
  kms   - kong migrations start; wipe/initialize the database and start Kong clean,
          optionally importing declarative configuration if available.
  kdbl  - kong start dbless; start Kong in dbless mode, requires a declarative configuration.
  ks    - kong start; starts Kong with the existing database contents (actually a restart).
  kp    - kong stop; stop Kong.
  kx    - export the current Kong database to a declarative configuration file.
  kauth - setup authentication (RBAC and GUI-auth).

[Kong-3.8.0:kong-plugin-example:/kong]$ 
```
shell が起動しました。DB-lessモードで実行するなら `kdbl`、 データストアを使うなら `kms` を最初の起動時に一回だけ実行します。以後は `ks` (`kong start`) だけで Kong が起動します。
```shell-session: kong migration start
[Kong-3.8.0:kong-plugin-example:/kong]$ kms  
Error: no such prefix: /kong-plugin/servroot

  Run with --v (verbose) or --vv (debug) for more details
prefix directory /kong-plugin/servroot not found, trying to create it
Database not bootstrapped, nothing to reset
Bootstrapping database...
migrating core on database 'kong_tests'...
core migrated up to: 000_base (executed)
core migrated up to: 003_100_to_110 (executed)
core migrated up to: 004_110_to_120 (executed)
core migrated up to: 005_120_to_130 (executed)
core migrated up to: 006_130_to_140 (executed)
core migrated up to: 007_140_to_150 (executed)
core migrated up to: 008_150_to_200 (executed)
core migrated up to: 009_200_to_210 (executed)
core migrated up to: 010_210_to_211 (executed)
core migrated up to: 011_212_to_213 (executed)
core migrated up to: 012_213_to_220 (executed)
core migrated up to: 013_220_to_230 (executed)
core migrated up to: 014_230_to_270 (executed)
core migrated up to: 015_270_to_280 (executed)
core migrated up to: 016_280_to_300 (executed)
core migrated up to: 017_300_to_310 (executed)
core migrated up to: 018_310_to_320 (executed)
core migrated up to: 019_320_to_330 (executed)
core migrated up to: 020_330_to_340 (executed)
core migrated up to: 021_340_to_350 (executed)
core migrated up to: 022_350_to_360 (executed)
core migrated up to: 023_360_to_370 (executed)
migrating acl on database 'kong_tests'...
acl migrated up to: 000_base_acl (executed)
acl migrated up to: 002_130_to_140 (executed)
acl migrated up to: 003_200_to_210 (executed)
acl migrated up to: 004_212_to_213 (executed)
migrating acme on database 'kong_tests'...
acme migrated up to: 000_base_acme (executed)
acme migrated up to: 001_280_to_300 (executed)
acme migrated up to: 002_320_to_330 (executed)
acme migrated up to: 003_350_to_360 (executed)
migrating ai-proxy on database 'kong_tests'...
ai-proxy migrated up to: 001_360_to_370 (executed)
migrating basic-auth on database 'kong_tests'...
basic-auth migrated up to: 000_base_basic_auth (executed)
basic-auth migrated up to: 002_130_to_140 (executed)
basic-auth migrated up to: 003_200_to_210 (executed)
migrating bot-detection on database 'kong_tests'...
bot-detection migrated up to: 001_200_to_210 (executed)
migrating hmac-auth on database 'kong_tests'...
hmac-auth migrated up to: 000_base_hmac_auth (executed)
hmac-auth migrated up to: 002_130_to_140 (executed)
hmac-auth migrated up to: 003_200_to_210 (executed)
migrating http-log on database 'kong_tests'...
http-log migrated up to: 001_280_to_300 (executed)
migrating ip-restriction on database 'kong_tests'...
ip-restriction migrated up to: 001_200_to_210 (executed)
migrating jwt on database 'kong_tests'...
jwt migrated up to: 000_base_jwt (executed)
jwt migrated up to: 002_130_to_140 (executed)
jwt migrated up to: 003_200_to_210 (executed)
migrating key-auth on database 'kong_tests'...
key-auth migrated up to: 000_base_key_auth (executed)
key-auth migrated up to: 002_130_to_140 (executed)
key-auth migrated up to: 003_200_to_210 (executed)
key-auth migrated up to: 004_320_to_330 (executed)
migrating oauth2 on database 'kong_tests'...
oauth2 migrated up to: 000_base_oauth2 (executed)
oauth2 migrated up to: 003_130_to_140 (executed)
oauth2 migrated up to: 004_200_to_210 (executed)
oauth2 migrated up to: 005_210_to_211 (executed)
oauth2 migrated up to: 006_320_to_330 (executed)
oauth2 migrated up to: 007_320_to_330 (executed)
migrating opentelemetry on database 'kong_tests'...
opentelemetry migrated up to: 001_331_to_332 (executed)
migrating post-function on database 'kong_tests'...
post-function migrated up to: 001_280_to_300 (executed)
migrating pre-function on database 'kong_tests'...
pre-function migrated up to: 001_280_to_300 (executed)
migrating rate-limiting on database 'kong_tests'...
rate-limiting migrated up to: 000_base_rate_limiting (executed)
rate-limiting migrated up to: 003_10_to_112 (executed)
rate-limiting migrated up to: 004_200_to_210 (executed)
rate-limiting migrated up to: 005_320_to_330 (executed)
rate-limiting migrated up to: 006_350_to_360 (executed)
migrating response-ratelimiting on database 'kong_tests'...
response-ratelimiting migrated up to: 000_base_response_rate_limiting (executed)
response-ratelimiting migrated up to: 001_350_to_360 (executed)
migrating session on database 'kong_tests'...
session migrated up to: 000_base_session (executed)
session migrated up to: 001_add_ttl_index (executed)
session migrated up to: 002_320_to_330 (executed)
66 migrations processed
66 executed
Database is up-to-date

Kong started
[Kong-3.8.0:kong-plugin-example:/kong]$ 
```
:::note
`Error: no such prefix: /kong-plugin/servroot`のエラーは、その後で作成されている旨メッセージが出ていて問題ありません(作成できなかった場合は本当にエラーになって `Kong started` になりません)。
:::

### Pongo 上での動作確認
#### プラグインのロード
起動した Pongo の shell 上で httpie (curl, wget でもOK) を使ってプラグインが読み込まれていることを確認できます。  
管理APIから、追加したプラグイン名のノードを検索し、指定したプライオリティとバージョンの内容が schema.lua で設定したものと同じになっていることで確認できます。

```shell-session: プラグインがロードされていることの確認
[Kong-3.8.0:kong-plugin-example:/kong]$ http -b http://localhost:8001/ |jq '.plugins.available_on_server."plugin-example"'
{
  "priority": 1000,
  "version": "0.0.1"
}
[Kong-3.8.0:kong-plugin-example:/kong]$ 
```
#### プラグインのテスト
テストシナリオを記述済みなら、`pongo shell` ではなく、いきなり `pongo run` でプラグインのテストを開始できます。
```shell-session: Pongo によるテストの実行
$ pongo run
[pongo-INFO] auto-starting the test environment, use the 'pongo down' action to stop it
[+] Running 2/2
 ✔ Network pongo-6a661833               Created                                                                                                                                                         0.1s 
 ✔ Container pongo-6a661833-postgres-1  Started                                                                                                                                                         0.3s 
[pongo-INFO] Waiting for 'postgres' to become healthy
Kong version: 3.8.0


Stopping after installing dependencies for plugin-example 0.0.1-1

[==========] Running tests from scanned files.
[----------] Global test environment setup.
[----------] Running tests from /kong-plugin/spec/plugin-example/01-simple_spec.lua
[ RUN      ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:62: plugin-example: (access) [#postgres] request gets no custom header
[       OK ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:62: plugin-example: (access) [#postgres] request gets no custom header (2.72 ms)
[ RUN      ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:78: plugin-example: (access) [#postgres] response gets a custom header
[       OK ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:78: plugin-example: (access) [#postgres] response gets a custom header (1.15 ms)
[ RUN      ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:62: plugin-example: (access) [#off] request gets no custom header
[       OK ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:62: plugin-example: (access) [#off] request gets no custom header (26.40 ms)
[ RUN      ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:78: plugin-example: (access) [#off] response gets a custom header
[       OK ] /kong-plugin/spec/plugin-example/01-simple_spec.lua:78: plugin-example: (access) [#off] response gets a custom header (1.18 ms)
[----------] 4 tests from /kong-plugin/spec/plugin-example/01-simple_spec.lua (26109.65 ms total)

[----------] Global test environment teardown.
[==========] 4 tests from 1 test file ran. (26110.58 ms total)
[  PASSED  ] 4 tests.
$ 
```
:::note warn
テストが完了したら `pongo down` を実行してデータストアのコンテナを終了させてください。
:::

# さいごに
今回は Pongo の実行と、Pongo上でのプラグインのテストの起動方法を説明しました。

次回は、肝心のテストのやり方を「[Kong Gatewayカスタムプラグインをテストする　～Part2: Pongoでテストする～](https://qiita.com/jirokichi2017/items/35ccee0e09ce80365920)」に書きます。