小ネタ:runnでローカルに立てたPostgreSQLサーバーに接続するテストを行う

runnでローカルに立てたPostgreSQLサーバーに接続しようとしたらうまくいかなかったので、なんとかした記録です。
2024.02.27

yamlでテストシナリオを書いてそのまま実行までできるAPIテストツールの新星 “runn” を試してみた

で紹介したシナリオテストツール"runn"ですが、DBに対するテストも行えるようです。

DBに結果を格納する|runnチュートリアル

今回はローカルに立てたPostgreSQLサーバーに接続しようとしてちょっと躓いたので、その対象方法を紹介します。

何がおきた?

公式の以下の箇所の記述を元に、ローカルに立てたPostgreSQLサーバーに接続しようとしました。

https://github.com/k1LoW/runn?tab=readme-ov-file#db-runner-query-a-database

書いたrunbookがこちらです。

db-connect-fail.yaml

desc: データベースを初期化する
runners:
  db: postgres://user:password@localhost:5432/dbname
steps:
  -
    desc: 接続確認
    db:
      query: SELECT 1
    dump: current.rows[0]

そして実行結果がこちらです。

実行結果

$ runn run db-connect-fail.yaml --debug   
Run "接続確認" on "データベースを初期化する".steps[0]
F

1) db-connect-fail.yaml bfcc572a0de6f7cbba77abdd77d99d73428aa5e1
  Failure/Error: db query failed on "データベースを初期化する".steps[0]: pq: SSL is not enabled on the server
  Failure step (db-connect-fail.yaml):
  5   -
  6     desc: 接続確認
  7     db:
  8       query: SELECT 1
  9     dump: current.rows[0]


1 scenario, 0 skipped, 1 failure

エラーの内容をよく見てみると、

pq: SSL is not enabled on the server

とあるので、SSLを使わないDB接続をする必要があるようです。

解決への道

問題を解決するため、まずはrunnのドキュメントを探してみましたが、それらしきものは見つかりませんでした。

となると、runnはOSSですので、実際にDB接続周りのコードを読むのが早そうです。早速見てみましょう。

まず、DB Runnerの定義はこちらのようです。

https://github.com/k1LoW/runn/blob/main/db.go#L40-L46

type dbRunner struct {
	name      string
	dsn       string
	client    TxQuerier
	hostRules hostRules
	trace     *bool
}

DB接続には dsn というフィールドが使われそうなことが見て取れます。

次に、このdsnを使う箇所を探してみましょう。すると、DBRunner構造体を作成するタイミングで、 dburl.Parse(dsn) という処理でパースが行われているようです。

https://github.com/k1LoW/runn/blob/main/db.go#L60-L69

func newDBRunner(name, dsn string) (*dbRunner, error) {
	_, err := dburl.Parse(dsn)
	if err != nil {
		return nil, err
	}
	return &dbRunner{
		name: name,
		dsn:  dsn,
	}, nil
}

では、dburlとは何なのか?と思ってコードを検索すると、 github.com/xo/dburl というパッケージのようです。

https://github.com/k1LoW/runn/blob/main/db.go#L3-L23

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync/atomic"
	"unsafe"

	"github.com/araddon/dateparse"
	_ "github.com/go-sql-driver/mysql"
	"github.com/goccy/go-json"
	"github.com/golang-sql/sqlexp"
	"github.com/golang-sql/sqlexp/nest"
	_ "github.com/googleapis/go-sql-spanner"
	_ "github.com/lib/pq"
	"github.com/xo/dburl"
	"modernc.org/sqlite"
)

GoのプログラムはGitHubリポジトリのパッケージを参照する文化があるので、パッケージ名を元にリポジトリを見てみましょう。

xo/dburl: Package dburl provides a standard, URL style mechanism for parsing and opening SQL database connection strings

dburlリポジトリのREADMEを見ると、Quickstartにそのものズバリの記載がありました。

import (
    "github.com/xo/dburl"
)

u, err := dburl.Parse("postgresql://user:pass@localhost/mydatabase/?sslmode=disable")
if err != nil { /* ... */ }

dsnの最後に?sslmode=disableを追加すればいけそうです。

やってみた

ということで、runbookに反映してやってみましょう。

db-connect-success.yaml

desc: データベースを初期化する
runners:
  db: postgres://postgres:password@localhost:5433/pzx?sslmode=disable
steps:
  -
    desc: 接続確認
    db:
      query: SELECT 1
    dump: current.rows[0]

実行結果がこちらです。

実行結果

$ runn run db-connect-success.yaml --debug
Run "接続確認" on "データベースを初期化する".steps[0]
-----START QUERY-----
SELECT 1
-----END QUERY-----
-----START QUERY RESULT-----
+----------+
| ?column? |
+----------+
|        1 |
+----------+
(1 row)
-----END QUERY RESULT-----
Run "dump" on "データベースを初期化する".steps[0]
{
  "?column?": 1
}
.

1 scenario, 0 skipped, 0 failures

無事、DB接続できて、SQLが実行できたようです。

まとめ

というわけで、「やってみた」ところ問題にぶつかったので、原因を探して対処してみました。 runnはOSSということで、なにか問題があったときに自分でコードを読みに行くことで対処できるのは、やっぱり良いですね。

今後もrunnを使いながら、なにか見つけたら順次ブログにしていきます。

余談:sslmodeの正体

https://github.com/xo/dburl?tab=readme-ov-file#database-schemes-aliases-and-drivers

を見ると、dburlがPostgreSQLの接続に利用しているのは、github.com/lib/pg というパッケージのようです。

https://github.com/lib/pq

このリポジトリ内を検索したところ、libpgと同様にPostgreSQLの接続パラメータが指定できると記載がありました。

https://github.com/lib/pq/blob/3d613208bca2e74f2a20e04126ed30bcb5c4cc27/doc.go#L42-L59

For compatibility with libpq, the following special connection parameters are
supported:

	* dbname - The name of the database to connect to
	* user - The user to sign in as
	* password - The user's password
	* host - The host to connect to. Values that start with / are for unix
	  domain sockets. (default is localhost)
	* port - The port to bind to. (default is 5432)
	* sslmode - Whether or not to use SSL (default is require, this is not
	  the default for libpq)
	* fallback_application_name - An application_name to fall back to if one isn't provided.
	* connect_timeout - Maximum wait for connection, in seconds. Zero or
	  not specified means wait indefinitely.
	* sslcert - Cert file location. The file must contain PEM encoded data.
	* sslkey - Key file location. The file must contain PEM encoded data.
	* sslrootcert - The location of the root certificate file. The file
	  must contain PEM encoded data.

これを受けて、libpq のドキュメントを探したところ、指定できる値にちゃんと記載がありました。

表 31-1. SSLモードの説明 ー SSLサポート ー 第 31章libpq - C ライブラリ ー PostgreSQL 9.4.5文書