GoでVCLの構文解析器を作った

GoでVCLのためのParserを作った話をします。

December 11, 2019
vcl golang fastly varnish

こんにちは。本記事はGo 7 Advent Calanderの11日目の記事になっています。

本記事ではGo言語によってVCLの構文解析器を作った話をします。

VCLって

Varnishのロゴ(かわいい...)

VCLとはVarnish Configuration Languageの略で、主にVarnishの設定をするために使われます。またCDNのFastlyでCustom VCLでFastlyの設定をすることができます。

動機

Go言語での構文解析器が世界でなかったからです。 VCLは学生のころから使ってきたのもあって、今回作ることにしました。

他の言語であるのは確認はしていませんが、見る感じなさそうです。

製作期間

この2日間と、日曜日に少し時間があったのでやりました。 日常の業務と匿名SlackチャンネルのためのBot作成やあったため、あまり時間を取れませんでした。

リポジトリ

以下のリポジトリで確認していただけます。
モチベーションになるのでよければStarをお願いします🙇

デモ

本当に簡単にしたかったです。VCLをデコードするのが目的でなく、デコードをしたあとがユーザーの本来の関心のため、構文解析のためのAPIを非常に簡単にしたかったです。

それで非常に簡潔に終わるものが出来上がりました。

例えばVCLが以下のようにあるとします。

acl purge_ip {
    "localhost";
    "127.0.0.1";
}

Go言語で構文解析にかけ、デコードするには以下のようにします。

type Root struct {
	ACls []*ACL `vcl:"acl,block"`
}

type ACL struct {
	Type      string   `vcl:"type,label"`
	Endpoints []string `vcl:"endpoints,flat"`
}

func main() {
	dat, err := ioutil.ReadFile("./example/vcl.vcl")
	if err != nil {
		log.Fatal(err)
	}

	r := &Root{}
	if errs := vcl.Decode(dat, r); len(errs) > 0 {
		log.Fatal(errs)
	}
}

非常に簡単です。

設計ポイント

1. Struct Tagの利用

JSONやYAMLなどのパッケージもそうなのですが、うまく構造体タグを使用しています。 今回の構造解析器にもvclタグを導入してattr, block, flatを定義しました。

特にflatが特徴的です。例えばVCLには

acl local {
    "198.0.0.1"
}

など、そのレベルでべたに(=flat)書くものがあるので、このようなものを導入しました。

2. CIDR記法や1sなど時間記法のサポート

CIDR記法は"233.93.0.0"/19などstringで扱うには"で途中が区切れるので面倒でした。しかし\"\"で囲ってしました。

また、1sなどの表記もあり、これはstringで片付けてしましたが、いつでもtime.Timeに変更できる仕様にはしました。しかし、ユーザーに任せてます。

3. フィールドをユーザーに任せる

1時間ぐらい葛藤したのですがbackendブロックはフィールドとなる概念があり、.ではじまります。

backend server2 {
    .host = "server2.example.com";
    .probe = {
        .url = "/";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

最初はvclタグにfieldをサポートしていてvcl:"host,fieldと定義していました。しかし.probeのフィールドであるように右辺が、オブジェクトの可能性があります。 そのため、構文解析器の拡張性も踏まえvclタグにはフィールド名をそのまま書く用に定義しました。(e.g. vcl:".host"など)

展望

今後の展望としてはアルゴリズムの最適化、構文解析器のUXをあげるためのフィードバック機構を作っていこうかと思います。

アドベントカレンダーは原稿を落とすと信用を必然的に失うので、間に合わせたく、リファクタリングなどの時間をとることはできませんでした。 もちろんテスト駆動開発(TDD)を中心に開発したため、テストはあるので整理していきたいと思います。

あと、条件式やサブルーチンのこれからやっていきます。

まとめ

構文解析器も特に難しいことはなく、非常にシュッと作れます。ただ条件式や、参照が多い言語では難しくなり、入力の言語によって難易度が大きく異なります。 時間をみつけて、徐々に改良していきます。アルゴリズムの最適化によって、スループットの向上もいずれしたいなと思います。

最後までありがとうございました!