フューチャー技術ブログ

Go Cloud#4 URLを編集するパッケージ

昨日に引き続きTIG DX Unitの渋川です。


これまで、基本的な機能を一通り紹介してきました。

Go Cloudでは何かとURL扱います。特にポータブルなAPIを使うと、mem://my-pub-sub やら s3://my-bucketやらで、URLでリソースの種類とその実態を指定しますし、場合によっては ?filename=collection.dbのようなクエリーで属性を設定したりします。

一方で、設定ファイルやらで正確なURLをすべてきちんと入れるのは大変です。

DocStoreでテーブルを10個使うアプリケーションがあり、データベース名までは同じだけど、コレクション名(≒RDBのテーブル)だけ置き換えたいみたいなケースでは、データベース名のところまでの1つだけを設定値として持ち、各テーブルのURLはプログラマブルに作りたいですよね?しかし、どこを書き換えればコレクション名が変わるかなど、細かいルールがバックエンドのドライバーごとに解釈が違うという難点があります。

ということでGo Cloud用のURL正規化パッケージを作りました。

このパッケージには、ちょっと省略形で書いたURLをGo Cloudで解釈できるURLに補完したり、一部書き換えたりといった関数が含まれています。なお、どの関数も、正規表現やテンプレートのパッケージと同じく、Mustが接頭辞についた関数も提供しております。

BlobのURLの正規化

NormalizeBlobURL(srcUrl string) (string, error)がBlobのURLの正規化の関数です。コマンドラインツールでユーザーが入力するフォルダ名をfile://folder形式にするといったちょっとした修正をします。Go Cloud、Cloudだけで使うのはもったいなので、CLIでも使おうと思って追加しました。リージョン名はAWS_REGIONという環境変数があればそこから補完します。

  • memmem://
  • folderfile://folder
  • s3://my-buckets3://my-bucket?region=us-west-1

DocStoreの正規化

NormalizeDocStoreURL(srcUrl string, opt Option) (string, error)という関数を提供しています。

DocStoreの場合、パスの階層構造やら主キーの設定の方法がバックエンドによって異なったりします。コレクション名は書き換えたいが、主キーはこのカラム名にしたい、みたいなのはこの関数を使えば一元化されます。

オプションは次の形式になっています。

type Option struct {
KeyName string // 主キー名(デフォルトは_id)
PartitionKey string // DynamoDBのパーティションキー(後述)
Collection string // コレクション名を上書きしたい場合
}

例えば、memdocstoreバックエンドの場合は次のようなURLが生成されます。

goclodurls.NormalizeDocStoreURL("mem://", goclodurls.Option{
Collection: "addresses",
})
// "mem://addresses/_id"

FireStoreの場合は、プロジェクト名/コレクションといった短い名前や、プロジェクト名/ドキュメント名/コレクション名といった短い名前を渡しても、Go Cloudが期待する名前(projectsやdatabasesやdocumentsというパスを挿入、データベース名のデフォルトは(default)を利用する)に書き換えます。最初からvalidな本来のURLを入れることも可能です。

goclodurls.NormalizeDocStoreURL("firestore://my-project", goclodurls.Option{
Collection: "addresses",
})
// "firestore://projects/my-project/databases/(default)/documents/addresses?name_field=_id"
goclodurls.NormalizeDocStoreURL("firestore://my-project/my-documents/addresses", goclodurls.Option{})
// "firestore://projects/my-project/databases/my-documents/documents/addresses?name_field=_id"

DynamoDBはパーティションキーだけの場合はパーティションキーが主キーになり、パーティションキーとソートキーがあると複合主キーのようになるという特性があります。本ライブラリでは、PartitionKeyが空文字列なら前者のモード(KeyName=パーティションキー)に、PartitionKeyが入っていれば後者のモード(KeyName=ソートキー、PartitionKey=パーティションキー)になるようにしています。

goclodurls.NormalizeDocStoreURL("dynamodb://", goclodurls.Option{
Collection: "tasks",
})
// "dynamodb://tasks?partition_key=_id"
goclodurls.NormalizeDocStoreURL("dynamodb://", goclodurls.Option{
Collection: "tasks",
PartitionKey: "job_id"
})
// "dynamodb://tasks?partition_key=job_id&sort_key=_id"

PubSubの正規化

NormalizePubSubURL(srcUrl string) (string, error)という関数を提供しています。これも今までのものとほぼ同じです。SNSはARNで書いても補完するし、SQSもhttpsのURLを書いても補完します。GCP PubSubはFirestoreと同様に固定のprojects/topicsを省略しても補完します。

gocloudurls.NormalizePubSubURL("arn:aws:sns:us-east-2:123456789012:mytopic")
// "awssns:///arn:aws:sns:us-east-2:123456789012:mytopic?region=us-east-2"

gocloudurls.NormalizePubSubURL("https://sqs.us-east-2.amazonaws.com/123456789012/myqueue")
// "awssqs://https://sqs.us-east-2.amazonaws.com/123456789012/myqueue?region=us-east-2"

gocloudurls.NormalizePubSubURL("gcppubsub://myproject/mytopic")
// "gcppubsub://projects/myproject/topics/mytopic"

おまけ

type Person struct {
Name string `docstore:"name"`
Age int
}

このようなdocstoreをマッピングする構造体があったとすると次の関数で、テーブル定義のコマンドライン引数を生成します。

ds, err := gocloudurls.NewDynamoDBSchema(&Person{},
gocloudurls.MustMustNormalizeDocStoreURL("dynamodb://persons"))

ds.CreateTableCommand()
aws dynamodb create-table --table-name persons \
--attribute-definitions AttributeName=name,AttributeType=S \
--key-schema AttributeName=name,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5

そのうち、Terraformのコードも生成したり、awscliを実行をしたり、というのも追加したいな、と思っています。

まとめ

最初のエントリーで紹介したように、クレデンシャルの問題以外を除けばマルチクラウドの利点を生かしたい場合はポータブル版のURLで扱うのがお手軽なことが多いです。しかし、特にDocStoreにおいて、URLのルールがバックエンドごとに違いすぎて、ポータブル版のAPIを扱うのが難しいというのを感じましたので、URLの操作もマルチクラウドにするためのユーティリティを実装しました。これでよりマルチクラウドが行いやすくなったと思います。