UNS Governance
このプラグインは、ACLチェック時にUnified Namespaceトピック構造を強制します。
プラグインAPI
ベースパス: /api/v5/plugin_api/emqx_unsgov
ブートストラップモデル
- 起動時に、UNS Governanceは
priv/bootstrap_models/*.jsonをスキャンします。 - 各ブートストラップモデルについて:
- その
idがデータベースに存在しない場合、プラグインはモデルを保存し、アクティブとしてマークします。 - 既にデータベースに
idが存在する場合、プラグインは読み込みをスキップし、情報レベルでログを出力します。
- その
- バンドルされたデフォルトのブートストラップモデル:
priv/bootstrap_models/model-v1.json
注意:ブートストラップモデルはクラスター内で最初のプラグイン起動時にデータベースにロードされます。後続のプラグインまたはノードの再起動では再ロードされません。モデルの更新はAPIを使用してDB内のモデルストアを更新してください。
JSONデータエンドポイント
GET /status: プラグインのステータス(on_mismatch、exempt_topics)。GET /stats: クラスター集計済みカウンターと最近のドロップ情報。GET /models: 保存されているすべてのモデルの一覧(各エントリにactiveフラグを含む)。GET /models/:id: 指定IDのモデルを取得。存在しない場合は404。POST /models: モデルの作成または更新。オプションでactivateフラグを指定可能。POST /models/:id/activate: 保存済みモデルをアクティブ化。POST /models/:id/deactivate: モデルを非アクティブ化。DELETE /models/:id: 保存済みモデルを削除。POST /validate/topic: アクティブモデルに対してトピックのバリデーションを実施。
その他のエンドポイント
GET /ui: インタラクティブなモデルエディターUI。GET /metrics: Prometheusテキストエクスポート形式。
UNSモデルスキーマ
このセクションでは、UNS Governanceが受け入れる完全なモデルJSONフォーマットを定義します。
トップレベルキー
id(必須、文字列):モデルID。正規表現^[A-Za-z0-9_-]+$にマッチする必要があります。評価順序はIDのアルファベット順で制御されます。name(任意、文字列):モデルの表示名。省略時はidと同じになります。variable_types(任意、オブジェクト):再利用可能な変数制約。tree(必須、オブジェクト):トピックツリーの定義。payload_types(任意、オブジェクト):再利用可能なペイロードスキーマ。
variable_types
変数タイプ名を制約オブジェクトにマッピングします。
サポートされる形式:
- 文字列正規表現マッチャー:
{"type":"string","pattern":"^...$"}
- 列挙型マッチャー:
{"type":"enum","values":["A","B","C"]}
変数タイプが存在しないか無効な場合、マッチャーは許容的な any にフォールバックします。
payload_types
ペイロードスキーマ名をスキーマオブジェクトにマッピングします。
バリデーションにはJSON Schemaを使用し、以下の互換性パッチがあります:
- トップレベルの
typeが省略されている場合、UNS Governanceは"object"にパッチを当てます。 - トップレベルのペイロードスキーマはオブジェクトルートでなければなりません。プリミティブルートは拒否されます。
これにより、以下の両方が可能です:
- 完全な自己完結型のオブジェクトJSON Schema。
- 既存の省略形オブジェクトスキーマ(例:
required/propertiesのみ)。
エンドポイントのペイロードバインディング:
- エンドポイントの
_payloadはpayload_typesのキーを参照するか、検証をスキップするために"any"を指定できます。
tree
tree は、各キーがルートトピックセグメントであり、各値がノードオブジェクトであるオブジェクトです。
ノードオブジェクトのキー:
children(任意、オブジェクト):子セグメントのマップ。_payload(任意、文字列):エンドポイントノードのペイロードタイプ名。デフォルトは"any"。_type(任意、互換性用):明示的なnamespace | variable | endpoint。_var_type(任意、互換性用):変数タイプ名。
ノードタイプの推論:
childrenが存在する場合:ノードは非エンドポイント。childrenが存在しない場合:ノードはエンドポイント。- 非エンドポイントキーの場合:
- キー
{name}は変数ノード - キー
+は変数ワイルドカードノード - その他のキーはネームスペースノード
- キー
変数タイプの解決:
- キー
{name}の場合:_var_typeがあれば使用- なければ推論されたタイプ名
nameを使用
- キー
+の場合:- マッチャーは
any(1セグメントにマッチ)
- マッチャーは
ツリー内のワイルドカードキー:
+:正確に1トピックセグメントにマッチ。#:残りのトピックセグメントすべてにマッチ(0セグメントも含む)。
完全な例
{
"id": "model-v1",
"name": "UNS Model V1",
"variable_types": {
"site_id": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_]{0,31}$" },
"line_id": { "type": "string", "pattern": "^Line[0-9]{1,4}$" },
"mode": { "type": "enum", "values": ["auto", "manual"] }
},
"payload_types": {
"line_control": {
"type": "object",
"required": ["Status", "Mode"],
"properties": {
"Status": { "type": "string", "enum": ["running", "stopped"] },
"Mode": { "type": "string", "enum": ["auto", "manual"] }
},
"additionalProperties": false
}
},
"tree": {
"default": {
"children": {
"{site_id}": {
"children": {
"Lines": {
"children": {
"{line_id}": {
"children": {
"LineControl": { "_payload": "line_control" }
}
}
}
},
"stream": {
"children": {
"#": { "_payload": "any" }
}
}
}
}
}
}
}
}強制動作
UNS Governanceはトピック構造と(オプションで)ペイロードスキーマの両方を検証します。
トピック違反(
topic_nomatch、topic_invalid、not_endpoint):topic_nomatch:アクティブなモデルのトピックフィルターがトピックにマッチしなかった。 (モデル固有の検証は実行されません。) アクティブモデルが存在せずUNS Governanceが有効な場合、exempt_topicsを除きトピックはフェイルクローズでtopic_nomatchとして扱われます。topic_invalid:選択されたモデルのフィルターはマッチしたが、トピックがモデルの構造・セグメント制約に違反。not_endpoint:選択されたモデルはトピックパスにマッチしたが、対象ノードがエンドポイントではない。- QoS 0:メッセージは無視されます。
- QoS 1/2:パブリッシュは拒否され、クライアントにプロトコル理由コード(
Not Authorized)が返されます。 - EMQXの
authorization.deny_actionがdisconnectに設定されている場合、トピック認可失敗時にクライアントは切断されます(設定はdisconnectでありdropではありません)。 authorization.deny_actionがデフォルトのignoreの場合、切断は行われませんが、QoS 1/2は拒否理由コードを受け取ります。- 監視可能なカウンター:
messages_dropped、topic_nomatch、topic_invalid、not_endpoint、およびモデルごとのカウンターper_model。
ペイロード違反(
payload_invalid):- メッセージはUNS Governanceによってパブリッシュ処理中にドロップされます。
- この経路では認可拒否や切断は不要です。
- 監視可能なカウンター:
messages_dropped、payload_invalid、およびモデルごとのカウンターper_model。
トピックフィルタープリチェック
複数モデルがアクティブな場合、UNS Governanceは完全な検証前にモデルをプリスクリーニングします:
- 各モデルはそのツリーパスから派生したトピックフィルターパターンにコンパイルされます。
- 変数セグメントは単一レベルワイルドカード(
+)に変換されます。- 例:
foo/{bar}/xはfoo/+/xになります。
- 例:
- アクティブモデルはモデルID順に並べられます。
- UNS Governanceはパブリッシュトピックにマッチする最初のモデル(ID順)を選択します。
- プリチェックは直接的なトピック/フィルターマッチングのみを使用し、パブリッシュトピックのプレフィックス展開(例:
/#の付加)は行いません。 - 選択されたモデルのみが完全に検証され、UNS Governanceは次のモデルに進みません。
- プリチェックに失敗したモデルはスキップされ、モデルごとのドロップカウンターに寄与しません。
これにより、無関係なアクティブモデルがカウンターを膨らませることを防ぎ、モデルの動作を決定論的に保ちます。また、モデル間でトピックツリーの重複を避けるべきことを意味します。
カウンター
GET /stats はクラスター集計済みカウンターを返します。
トップレベルカウンター:
messages_total:処理されたメッセージ合計(messages_allowed + messages_dropped)。免除トラフィックも含む。messages_allowed:許可されたメッセージと免除されたメッセージの合計。messages_dropped:UNS検証失敗によりドロップ/拒否されたメッセージ。topic_nomatch:アクティブモデルフィルターにマッチしなかったためドロップ/拒否。topic_invalid:選択モデルのトピック不一致によりドロップ/拒否。not_endpoint:トピックが非エンドポイントノードにマッチしたためドロップ/拒否。payload_invalid:ペイロードスキーマ不一致によりドロップ。exempt:exempt_topicsによりスキップされたメッセージ。per_model:モデルIDをキーとしたモデル別内訳マップ。recent_drops:最近のドロップイベント(topic、error_type、error_detail、timestamp_ms)。
モデル別カウンター(per_model.<model_id>):
messages_totalmessages_allowedmessages_droppedtopic_invalidnot_endpointpayload_invalid
カウンターの意味:
record_allowedは該当モデルのmessages_totalとmessages_allowedを増加させます。- トピック/ペイロードのドロップは該当モデルの
messages_total、messages_dropped、および該当理由カウンターを増加させます。 - トピックフィルタープリチェックでどのモデルも合格しなかった場合、グローバルに
topic_nomatchが増加し、モデル別ドロップカウンターは増加しません。 これはアクティブモデルセットが空の場合も含みます。
ダウンロード
各EMQXリリースのtarball:
| EMQXバージョン | プラグインバージョン | パッケージ |
|---|---|---|
| 6.2.0 | 0.1.2 | emqx_unsgov-0.1.2.tar.gz |
| 6.2.1 | 0.1.3 | emqx_unsgov-0.1.3.tar.gz |