🌸

さくらのクラウドのAPIで名前で完全一致させる方法

2021/01/13に公開

2021-01-20 追記:今日 usacloudv1.1.0 がリリースされ、 usacloud だけで出来ることが増えたので大幅に書き換えました。

usacloud では名前はデフォルトでは部分一致になります

さくらのクラウド の API を利用するには usacloud という CLI (コマンドラインインターフェース)が便利です。作者の yamamoto-febc さん、ありがとうございます!

usacloud は README の「基本的な使い方」に

usacloud <リソース> <サブコマンド> [オプション] [対象リソースのID or 名前(部分一致) or タグ]

とある通り、名前を指定する場合は部分一致という仕様になっています。

これは通常は便利かもしれませんが、例えば DNSオプションサービス ではドメイン名を指定するとサブドメインもマッチしてしまうので完全一致にしたいというケースもあります。

usacloud v1.1.0 からはオプション定で名前でも完全一致にできます

作者の yamamoto-febc さんに相談したところ usacloud の v1.1.0 では --argument-match-mode というオプションが追加されました。

デフォルト値は partial で部分一致ですが exact を指定すれば完全一致になります。

例えば example.com というドメイン名で完全一致でDNS情報をJSON形式で出力するには以下のようにします。

usacloud dns read --output-type json --argument-match-mode exact example.com

usacloud v1.1.0 ではオプション指定で jq 形式のクエリで出力のJSONをフィルタリングできます

v1.0.3 までは --query オプションで JMESPath のクエリを指定して出力のJSONを絞り込むことが出来ました。

v1.1.0 からは itchyny さんの gojq: Pure Go implementation of jq を使って jq のクエリを指定できるようになりました(itchyny さん素晴らしいCLIとライブラリ実装ありがとうございます!)。

v1.1.0 から追加された --query-driver オプション(デフォルト値は jmethpath)に jq と指定しつつ --query オプションに jq のクエリを書くことで利用できます。

例を示します。

usacloud dns read --output-type json --argument-match-mode exact --query-driver -jq --query '.[0].Records' example.com

横に長くてスクロールしないと見れないので引用でも書いておきます。

usacloud dns read --output-type json --argument-match-mode exact --query-driver -jq --query '.[0].Records' example.com

さらにこだわる人向け:API のフィルタリングでサーバ側で名前で完全一致も可能

通常の用途では上記の使い方で十分で、以下の対応は不要です。

usacloud では引数にリソースIDか名前かタグを指定できるという仕様の関係でサーバ側ではなくクライアント側でフィルタリングしています。

フィルタリング前の状態で大量な件数にヒットする場合は、理想はサーバ側で完全一致でフィルタリングしたいところです。

API ドキュメント の「リクエストパラメータの共通仕様」の「フィルタリング」の項を読んでみると、「文字列型カラムは中間一致」ですが「配列を与えると完全一致のOR結合」になるということがわかりました。

例えば Name のフィールドが example.com に完全一致するようなフィルタリングは {"Filter":{"Name":["example.com"]}} と指定すれば良いわけです。

API ドキュメントのリクエスト例では

GET /cloud/1.1/server
{
    Filter: {
        "Name": "test example", // Name に test と example を両方含みます
        "Zone.Name": [ "is1a", "is1b" ], // Zone.Name が is1a または is1b に完全一致
        "CreatedAt <": "2011-09-01T00:00:00+0900"// 作成日時がこの時刻より前(演算子は時刻・数値カラムのみ使用可) 
         // (これらをすべて満たすレコードが抽出されます)
}

のようにリクエスト行の後に JSON が書かれていますが、実際には URL エンコードしてクエリストリングとして指定する必要があります。

usacloud の rest コマンドでさくらのクラウドAPIを直接呼べる

usacloud の restコマンド を使うとさくらのクラウドAPIを直接呼び出すことができます。

また rest コマンドの -d オプションを使うと JSON をURLエンコードしてクエリストリングとして指定もしてくれます。

フィルタリングの条件に "ServiceClass":"cloud/dns" も追加して {"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"}} としこれをエンコーディングして下記のようにすれば指定したドメイン名で完全一致するDNSの設定情報が取得できます。

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"}}' '/commonserviceitem'

(上記と同じ内容を引用で再掲)

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"}}' '/commonserviceitem'

usacloudrest コマンドでも上で紹介したのと同様に --query-driver jq--query jq形式のクエリ を指定することでフィルタリングも出来ます。例を示します。

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"}}' --query-driver jq --query '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets' /commonserviceitem

(上記と同じ内容を引用で再掲)

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"}}' --query-driver jq --query '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets' /commonserviceitem

さくらのクラウドのAPIの「取得キーの選択」

DNS レコードだけとか ID だけ取得したい場合は、 API ドキュメントの「取得キーの選択」で "Include":["Settings"]"Include":["ID"] を追加して絞り込みが出来ます。ただし、必ずついてくる項目もあるので --query でさらに絞り込む必要があります。

ドメイン名完全一致で ID を取得する例

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["ID"]}' --query-driver jq --query '.CommonServiceItems[0].ID' /commonserviceitem

(上記と同じ内容を引用で再掲)

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["ID"]}' --query-driver jq --query '.CommonServiceItems[0].ID' /commonserviceitem

出力例

999999999999

ドメイン名完全一致で DNS レコードを取得する例

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["Settings"]}' --query-driver jq --query '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets' /commonserviceitem 

(上記と同じ内容を引用で再掲)

usacloud rest request -d '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["Settings"]}' --query-driver jq --query '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets' /commonserviceitem

出力例

[
  {
    "Name": "api",
    "RData": "192.0.2.1",
    "TTL": 3600,
    "Type": "A"
  },
…(略)…
]

参考:curl と jq だけで同じことをする方法

jq での URL エンコード

bash - How to urlencode data for curl command? - Stack Overflow回答 で知ったのですが stedolan/jq: Command-line JSON processor で以下の例のようにして URL エンコードすることが出来ます。

$ printf %s 'encode this'|jq -sRr @uri
encode%20this
$ jq -rn --arg x 'encode this' '$x|@uri'
encode%20this

これを使うと curl と jq だけでも同じことを実現できます。
もちろん jq の代わりに gojq でも大丈夫です。

ACCESS_TOKEN, ACCESS_TOKEN_SECRET, API_URLさくらのクラウド API v1.1 ドキュメント の「はじめに」を参照しつつ以下のように環境変数を設定しておくこととします。

export ACCESS_TOKEN=コントロールパネルのAPIキーのアクセストークン
export ACCESS_TOKEN_SECRET=コントロールパネルのAPIキーのアクセストークンシークレット
export API_URL=https://secure.sakura.ad.jp/cloud/zone/tk1a/api/cloud/1.1/ # DNSはゾーン無関係ですが東京第1ゾーンを指定

ドメイン名完全一致で ID を取得する例

curl -sS -u "$ACCESS_TOKEN:$ACCESS_TOKEN_SECRET" "$API_URL/commonserviceitem?"$(jq -rn --arg x '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["ID"]}' '$x|@uri') | jq -r '.CommonServiceItems[0].ID'

(上記と同じ内容を引用で再掲)

curl -sS -u "$ACCESS_TOKEN:$ACCESS_TOKEN_SECRET" "$API_URL/commonserviceitem?"$(jq -rn --arg x '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["ID"]}' '$x|@uri') | jq -r '.CommonServiceItems[0].ID'

(本題と関係ないのですが、 jq の前の $ をバックスラッシュでエスケープしないと KaTeX parse error: Double superscript at position 82 という感じで zenn cli でのプレビュー結果内にエラーが出ました。シングルクォート内の $ はそのままで良いけどクォートの外かダブルクォート内の $ はエスケープが必要なようです)

ドメイン名完全一致で DNS レコードを取得する例

curl -sS -u "$ACCESS_TOKEN:$ACCESS_TOKEN_SECRET" "$API_URL/commonserviceitem?"$(jq -rn --arg x '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["Settings"]}' '$x|@uri') | jq '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets'

(上記と同じ内容を引用で再掲)

curl -sS -u "$ACCESS_TOKEN:$ACCESS_TOKEN_SECRET" "$API_URL/commonserviceitem?"$(jq -rn --arg x '{"Filter":{"Name":["example.com"],"ServiceClass":"cloud/dns"},"Include":["Settings"]}' '$x|@uri') | jq '.CommonServiceItems[0].Settings.DNS.ResourceRecordSets'

Discussion