[Feature] Add PPL rest command#5599
Conversation
Add a leading `rest <endpoint>` command that exposes a curated, read-only, fixed-schema set of in-cluster management endpoints as a PPL table, modeled as a system row source bridged through visitRelation (the same seam as describe and the system-index family), so it runs on the Calcite engine without the unsupported table-function path. - Grammar/AST: REST/TIMEOUT tokens, restCommand rule, RestRelation, AstBuilder visit, query anonymizer. - Execution: RestSourceTable -> CalciteLogicalRestScan / CalciteEnumerableRestScan; RestEndpointRegistry (read-only allow-list + fixed schema + accepted args); RestEnumerator/RestRequest dispatch via OpenSearchClient (NodeClient in-cluster, RestClient standalone). - 9 endpoints: cluster health/state/settings, cat indices/nodes/cluster_manager/ plugins/shards, resolve/index. - Output shaping: numeric type normalization, id-to-name resolution, role-name expansion, structural flattening, graceful null degrade. - Args: count caps emitted rows; timeout reserved but rejected with 400; get-args applied server-side with per-arg value validation (local on health, health on cat/indices, expand_wildcards on resolve/index). Undeclared arg or out-of-domain value is rejected with a 400. level and include_defaults are deferred to a later release; flat_settings is dropped as redundant. - Error handling: blank endpoint, negative count, disallowed arg, and uncoercible value all surface clean 400s rather than 500s. Tests: CalcitePPLRestIT 25/25, RestEndpointRegistryTest 16, RestSourceTableTest 10. Signed-off-by: Louis Chu <clingzhi@amazon.com>
PR Code Analyzer ❗AI-powered 'Code-Diff-Analyzer' found issues on commit 03d21c3.
The table above displays the top 10 most important findings. Pull Requests Author(s): Please update your Pull Request according to the report above. Repository Maintainer(s): You can Thanks. |
PR Reviewer Guide 🔍(Review updated until commit 03d21c3)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 03d21c3 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 4b6262f
Suggestions up to commit f4b6086
Suggestions up to commit 408c730
Suggestions up to commit 44c9bb0
Suggestions up to commit aef4663
|
…xes; comment cleanup - /_cluster/settings: run the persistent and transient tiers through the node SettingsFilter (published via RestSettingsFilterHolder from SQLPlugin#getRestHandlers) so Property.Filtered and plugin-registered pattern settings are redacted exactly as the native GET /_cluster/settings endpoint. Remove the dead secretFields column-filter, which was the wrong shape for the (setting, value, tier) rows. - Parser: add TIMEOUT to searchableKeyWord so a bare 'timeout' term still matches searchLiteral. - coerce(): narrow the catch to IllegalArgumentException | ClassCastException; add empty-string guards in toNumber/toBoolean. - spotlessApply formatting; drop outdated and redundant comments. Tests: RestEndpointRegistryTest, RestSourceTableTest, OpenSearchNodeClientClusterSettingsFilterTest green. Signed-off-by: Louis Chu <clingzhi@amazon.com>
|
Persistent review updated to latest commit a7a8a20 |
- clusterSettings: fail closed (throw IllegalStateException) when the node SettingsFilter is unavailable, instead of returning unredacted settings - collectSettings: handle list-type settings via getAsList fallback - decodeRestSpec: reject a blank/missing endpoint with a clear error - docs: correct rest.md allow-list table (9 endpoints + accepted args), quote endpoint literals, fix timeout/get-arg descriptions, add security note - register docs/user/ppl/cmd/rest.md in docs/category.json (deterministic single-node examples: number_of_nodes=1, cluster_manager count=1) Signed-off-by: Louis Chu <clingzhi@amazon.com>
|
Persistent review updated to latest commit d43f28c |
d43f28c to
a3b1a9e
Compare
Verify the rest command is subject to the security plugin fine grained access control: a caller without cluster:monitor privilege is denied the cat and cluster endpoints, a caller holding the privilege can run them, and the resolve index endpoint is filtered to the caller authorized indices. Test indices are created idempotently, and denials are asserted by the security denial reason in the response body because a denied action on the Calcite only rest path surfaces as a wrapped error carrying that reason. Calcite fallback is disabled so the denial reason is not replaced by an unsupported command error. Signed-off-by: Louis Chu <clingzhi@amazon.com>
a3b1a9e to
7d9df84
Compare
|
Persistent review updated to latest commit 7d9df84 |
plugins.ppl.rest.redaction.enabled and plugins.ppl.rest.allowed_endpoints were dynamic cluster settings, so they could be changed at runtime through _cluster/settings or the _plugins/_query/settings endpoint. On a managed deployment that let a caller disable redaction or widen the allow-list an operator had configured. Drop Setting.Property.Dynamic so both are node-level settings set in the node config; the engine rejects runtime updates on both paths. Register them without an update consumer and read the node-configured value. Signed-off-by: Louis Chu <clingzhi@amazon.com>
|
Persistent review updated to latest commit aef4663 |
| * @return one map of column name to value per index | ||
| */ | ||
| default List<Map<String, Object>> catIndices(Map<String, String> params) { | ||
| throw new UnsupportedOperationException("catIndices is not supported by this client"); |
There was a problem hiding this comment.
np: remove all these?
I tried this and had to revert it. It turns out these can't be removed. OpenSearchClient is also implemented downstream by opensearch-project/sql-cli (client/http5/OpenSearchRestClientImpl), which doesn't implement the rest command methods.
Ref https://github.com/opensearch-project/sql/actions/runs/29349113963/job/87140469074?pr=5599
| | `/_cluster/health` | `cluster_name` (string), `status` (string), `number_of_nodes` (integer), `number_of_data_nodes` (integer), `active_primary_shards` (integer), `active_shards` (integer), `relocating_shards` (integer), `initializing_shards` (integer), `unassigned_shards` (integer), `timed_out` (boolean) | `local` | | ||
| | `/_cluster/state` | `cluster_name` (string), `state_uuid` (string), `version` (long), `cluster_manager_node` (string) | (none) | | ||
| | `/_cluster/settings` | `setting` (string), `value` (string), `tier` (string) | (none) | | ||
| | `/_cat/indices` | `index` (string), `health` (string), `pri` (integer), `rep` (integer), `active_shards` (integer) | `health` | | ||
| | `/_cat/nodes` | `name` (string), `ip` (string), `node_role` (string), `heap_percent` (integer), `ram_percent` (integer), `cpu` (integer) | (none) | | ||
| | `/_cat/cluster_manager` | `id` (string), `host` (string), `ip` (string), `node` (string) | (none) | | ||
| | `/_cat/plugins` | `name` (string), `component` (string), `version` (string) | (none) | | ||
| | `/_cat/shards` | `index` (string), `shard` (integer), `prirep` (string), `state` (string), `node` (string) | (none) | | ||
| | `/_resolve/index` | `name` (string), `type` (string) | `expand_wildcards` | |
There was a problem hiding this comment.
I only see RestResponseRedactor focus on IP address redaction. But in my test (Dev Tools in AOS domain), I can see many other masked values, e.g., version in _cat/plugins, cluster.routing.allocation.awareness.force.zone in _cluster/settings, host in _cat/cluster_manager. Are all these redacted automatically? If not, any better way to avoid such risk, like sending what's in REST command go through the exact same process as a REST request sent to the domain by users?
There was a problem hiding this comment.
I only see
RestResponseRedactorfocus on IP address redaction. But in my test (Dev Tools in AOS domain), I can see many other masked values, e.g., version in _cat/plugins, cluster.routing.allocation.awareness.force.zone in _cluster/settings, host in _cat/cluster_manager. Are all these redacted automatically? If not, any better way to avoid such risk, like sending what's in REST command go through the exact same process as a REST request sent to the domain by users?
The redaction replicates the AOS-side logic exactly. As called out in the PR description, it masks more than IPv4 addresses — IPv6, inet[/…], EC2-style hostnames (ip-a-b-c-d), and availability-zone names are all covered. Every case is asserted in RestResponseRedactorTest.
|
Persistent review updated to latest commit f3a6226 |
f3a6226 to
aef4663
Compare
|
Persistent review updated to latest commit aef4663 |
| Pattern.compile("([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}", Pattern.CASE_INSENSITIVE); | ||
| private static final Pattern AZ_NAME = | ||
| Pattern.compile( | ||
| "(us(-(gov|iso[a-z]?))?|af|ap|ca|cn|eu|sa|me|il)-(central|(north|south)?(east|west)?)-\\d[a-z]", |
There was a problem hiding this comment.
we need to update this whenever there is a new region?
|
Persistent review updated to latest commit 44c9bb0 |
Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
44c9bb0 to
408c730
Compare
|
Persistent review updated to latest commit 408c730 |
408c730 to
f4b6086
Compare
|
Persistent review updated to latest commit f4b6086 |
f4b6086 to
4b6262f
Compare
|
Persistent review updated to latest commit 4b6262f |
Flip plugins.ppl.rest.allowed_endpoints default from ["*"] to empty so open source ships with the rest command closed. Deployments opt specific endpoints in via the setting (AOS enables the ones it supports; AOSS leaves it empty and stays disabled). Enforcement already treats an empty or missing list as disabled. Opt the integration-test clusters into all endpoints so the rest ITs still exercise the enabled path. Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
4b6262f to
03d21c3
Compare
|
Persistent review updated to latest commit 03d21c3 |
Description
Add a leading
rest <endpoint>command that exposes a curated, read-only, fixed-schema set of in-cluster management endpoints as a PPL table, modeled as a system row source bridged through visitRelation (the same seam as describe and the system-index family), so it runs on the Calcite engine without the unsupported table-function path.Response redaction, endpoint allow-list, and access control
Two node-level settings (
NodeScope, set inopensearch.yml) gate the command. The allow-list defaults to empty, so the command is disabled by default — a deployment must explicitly opt endpoints in. The redaction setting defaults to native (off). They are intentionally not dynamically updatable, so the gate cannot be turned off (or the allow-list widened) at runtime via_cluster/settingsor_plugins/_query/settingsonce an operator has set it.plugins.ppl.rest.redaction.enabled(Boolean, defaultfalse): when enabled, masks network identifiers in/_cat/*output — IPv4, IPv6,inet[/...]addresses, EC2-style host names (ip-a-b-c-d), and availability-zone names — and availability-zone names in/_cluster/settingsvalues. Defaultfalsepreserves the native_catoutput (real values).plugins.ppl.rest.allowed_endpoints(List, default[]): the command is disabled by default; a deployment opts endpoints in explicitly.[](the default) disables the command entirely; an explicit subset allows only those endpoints (others return a clear "not enabled on this cluster" 400);["*"]allows every registered endpoint. This lets open source ship closed while a managed service (for example AOS) enables only the endpoints it supports, and AOSS leaves it empty. Enforced at a single choke point inOpenSearchStorageEngine#getTable.Access control. The command is already subject to the security plugin's fine-grained access control with no special integration. Each endpoint dispatches a standard transport action (for example
cluster:monitor/nodes/stats,cluster:monitor/state,indices:admin/resolve/index) through the node client under the caller's identity, so the securityActionFilterauthorizes every call by action name. A caller lacking the privilege is rejected, exactly as on the native endpoint, and/_resolve/indexrequires theindices:admin/resolve/indexprivilege because the command resolves all indices. Document- and field-level security do not apply because the command issues no document searches, which matches the native_catand_clusterAPIs. Consequently, once a deployment opts endpoints in, behavior matches native 1:1 — real IPs and the enabled endpoints are visible only to callers who already hold the correspondingcluster:monitor/*privilege — while the empty-by-default allow-list and optional redaction are additional hardening for managed or restricted deployments.Related Issues
Resolves #5597
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.