Skip to content

Commit 37b7375

Browse files
committed
Add API specific endpoints for routing rules
1 parent 57d6dcf commit 37b7375

5 files changed

Lines changed: 118 additions & 29 deletions

File tree

docs/gateway-api.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,25 @@ curl -X POST http://localhost:8080/gateway/backend/activate/trino-2
124124

125125
## Update routing rules
126126

127-
This endpoint is part of the `/webapp` endpoint family and requires the
128-
`ADMIN` role.
129-
130127
The API can be used to programmatically update the routing rules. Rule are
131128
updated based on the rule name. Storage of the rules must use a writeable file
132-
and the configuration 'rulesType: FILE'.
129+
and the configuration `rulesType: FILE`.
133130

134131
For this feature to work with multiple replicas of the Trino Gateway, you must
135132
provide a shared storage that supports file locking for the routing rules file.
136133
If multiple replicas are used with local storage, then rules get out of
137134
sync when updated.
138135

136+
Get all routing rules:
137+
138+
```shell
139+
curl -X GET http://localhost:8080/gateway/routing-rules/all
140+
```
141+
142+
Update a routing rule:
143+
139144
```shell
140-
curl -X POST http://localhost:8080/webapp/updateRoutingRules \
145+
curl -X POST http://localhost:8080/gateway/routing-rules/modify/update \
141146
-H 'Content-Type: application/json' \
142147
-d '{ "name": "trino-rule",
143148
"description": "updated rule description",

gateway-ha/src/main/java/io/trino/gateway/ha/resource/GatewayResource.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
import com.google.inject.Inject;
1717
import io.airlift.log.Logger;
18+
import io.trino.gateway.ha.domain.RoutingRule;
1819
import io.trino.gateway.ha.router.GatewayBackendManager;
1920
import jakarta.annotation.security.RolesAllowed;
21+
import jakarta.ws.rs.Consumes;
2022
import jakarta.ws.rs.GET;
2123
import jakarta.ws.rs.POST;
2224
import jakarta.ws.rs.Path;
@@ -27,6 +29,8 @@
2729
import jakarta.ws.rs.core.Request;
2830
import jakarta.ws.rs.core.Response;
2931

32+
import java.io.IOException;
33+
3034
import static java.util.Objects.requireNonNull;
3135

3236
@RolesAllowed("API")
@@ -37,11 +41,13 @@ public class GatewayResource
3741
private static final Logger log = Logger.get(GatewayResource.class);
3842

3943
private final GatewayBackendManager gatewayBackendManager;
44+
private final RoutingRulesResourceHandler routingRulesResourceHandler;
4045

4146
@Inject
42-
public GatewayResource(GatewayBackendManager gatewayBackendManager)
47+
public GatewayResource(GatewayBackendManager gatewayBackendManager, RoutingRulesResourceHandler routingRulesResourceHandler)
4348
{
4449
this.gatewayBackendManager = requireNonNull(gatewayBackendManager, "gatewayBackendManager is null");
50+
this.routingRulesResourceHandler = requireNonNull(routingRulesResourceHandler, "routingRulesResourceHandler is null");
4551
}
4652

4753
@GET
@@ -92,6 +98,23 @@ public Response activateBackend(@PathParam("name") String name)
9298
return Response.ok().build();
9399
}
94100

101+
@GET
102+
@Path("/routing-rules/all")
103+
public Response getRoutingRules()
104+
throws IOException
105+
{
106+
return routingRulesResourceHandler.getRoutingRules();
107+
}
108+
109+
@POST
110+
@Consumes(MediaType.APPLICATION_JSON)
111+
@Path("/routing-rules/modify/update")
112+
public Response updateRoutingRules(RoutingRule routingRule)
113+
throws IOException
114+
{
115+
return routingRulesResourceHandler.updateRoutingRules(routingRule);
116+
}
117+
95118
private Response throwError(Exception e)
96119
{
97120
return Response.status(Response.Status.NOT_FOUND)

gateway-ha/src/main/java/io/trino/gateway/ha/resource/GatewayWebAppResource.java

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import io.trino.gateway.ha.clustermonitor.TrinoStatus;
2020
import io.trino.gateway.ha.config.HaGatewayConfiguration;
2121
import io.trino.gateway.ha.config.ProxyBackendConfiguration;
22-
import io.trino.gateway.ha.config.RoutingRulesConfiguration;
23-
import io.trino.gateway.ha.config.RulesType;
2422
import io.trino.gateway.ha.config.UIConfiguration;
2523
import io.trino.gateway.ha.domain.Result;
2624
import io.trino.gateway.ha.domain.RoutingRule;
@@ -40,7 +38,6 @@
4038
import io.trino.gateway.ha.router.HaGatewayManager;
4139
import io.trino.gateway.ha.router.QueryHistoryManager;
4240
import io.trino.gateway.ha.router.ResourceGroupsManager;
43-
import io.trino.gateway.ha.router.RoutingRulesManager;
4441
import jakarta.annotation.security.RolesAllowed;
4542
import jakarta.ws.rs.Consumes;
4643
import jakarta.ws.rs.GET;
@@ -76,30 +73,25 @@ public class GatewayWebAppResource
7673
private final QueryHistoryManager queryHistoryManager;
7774
private final BackendStateManager backendStateManager;
7875
private final ResourceGroupsManager resourceGroupsManager;
79-
private final boolean isRulesEngineEnabled;
80-
private final RulesType ruleType;
8176
// TODO Avoid putting mutable objects in fields
8277
private final UIConfiguration uiConfiguration;
83-
private final RoutingRulesManager routingRulesManager;
78+
private final RoutingRulesResourceHandler routingRulesResourceHandler;
8479

8580
@Inject
8681
public GatewayWebAppResource(
8782
GatewayBackendManager gatewayBackendManager,
8883
QueryHistoryManager queryHistoryManager,
8984
BackendStateManager backendStateManager,
9085
ResourceGroupsManager resourceGroupsManager,
91-
RoutingRulesManager routingRulesManager,
86+
RoutingRulesResourceHandler routingRulesResourceHandler,
9287
HaGatewayConfiguration configuration)
9388
{
9489
this.gatewayBackendManager = requireNonNull(gatewayBackendManager, "gatewayBackendManager is null");
9590
this.queryHistoryManager = requireNonNull(queryHistoryManager, "queryHistoryManager is null");
9691
this.backendStateManager = requireNonNull(backendStateManager, "backendStateManager is null");
9792
this.resourceGroupsManager = requireNonNull(resourceGroupsManager, "resourceGroupsManager is null");
9893
this.uiConfiguration = configuration.getUiConfiguration();
99-
this.routingRulesManager = requireNonNull(routingRulesManager, "routingRulesManager is null");
100-
RoutingRulesConfiguration routingRules = configuration.getRoutingRules();
101-
isRulesEngineEnabled = routingRules.isRulesEngineEnabled();
102-
ruleType = routingRules.getRulesType();
94+
this.routingRulesResourceHandler = requireNonNull(routingRulesResourceHandler, "routingRulesResourceHandler is null");
10395
}
10496

10597
@POST
@@ -461,12 +453,7 @@ public Response readExactMatchSourceSelector()
461453
public Response getRoutingRules()
462454
throws IOException
463455
{
464-
if (isRulesEngineEnabled && ruleType == RulesType.EXTERNAL) {
465-
return Response.status(Response.Status.NO_CONTENT)
466-
.entity(Result.fail("Routing rules are managed by an external service")).build();
467-
}
468-
List<RoutingRule> routingRulesList = routingRulesManager.getRoutingRules();
469-
return Response.ok(Result.ok(routingRulesList)).build();
456+
return routingRulesResourceHandler.getRoutingRules();
470457
}
471458

472459
@POST
@@ -477,8 +464,7 @@ public Response getRoutingRules()
477464
public Response updateRoutingRules(RoutingRule routingRule)
478465
throws IOException
479466
{
480-
List<RoutingRule> routingRulesList = routingRulesManager.updateRoutingRule(routingRule);
481-
return Response.ok(Result.ok(routingRulesList)).build();
467+
return routingRulesResourceHandler.updateRoutingRules(routingRule);
482468
}
483469

484470
@GET
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.trino.gateway.ha.resource;
15+
16+
import com.google.inject.Inject;
17+
import io.trino.gateway.ha.config.HaGatewayConfiguration;
18+
import io.trino.gateway.ha.config.RoutingRulesConfiguration;
19+
import io.trino.gateway.ha.config.RulesType;
20+
import io.trino.gateway.ha.domain.Result;
21+
import io.trino.gateway.ha.domain.RoutingRule;
22+
import io.trino.gateway.ha.router.RoutingRulesManager;
23+
import jakarta.ws.rs.core.Response;
24+
25+
import java.io.IOException;
26+
import java.util.List;
27+
28+
import static java.util.Objects.requireNonNull;
29+
30+
public class RoutingRulesResourceHandler
31+
{
32+
private final RoutingRulesManager routingRulesManager;
33+
private final boolean isRulesEngineEnabled;
34+
private final RulesType rulesType;
35+
36+
@Inject
37+
public RoutingRulesResourceHandler(RoutingRulesManager routingRulesManager, HaGatewayConfiguration configuration)
38+
{
39+
this.routingRulesManager = requireNonNull(routingRulesManager, "routingRulesManager is null");
40+
RoutingRulesConfiguration routingRulesConfiguration = requireNonNull(configuration, "configuration is null").getRoutingRules();
41+
this.isRulesEngineEnabled = routingRulesConfiguration.isRulesEngineEnabled();
42+
this.rulesType = routingRulesConfiguration.getRulesType();
43+
}
44+
45+
public Response getRoutingRules()
46+
throws IOException
47+
{
48+
if (isRulesEngineEnabled && rulesType == RulesType.EXTERNAL) {
49+
return Response.status(Response.Status.NO_CONTENT)
50+
.entity(Result.fail("Routing rules are managed by an external service")).build();
51+
}
52+
List<RoutingRule> routingRulesList = routingRulesManager.getRoutingRules();
53+
return Response.ok(Result.ok(routingRulesList)).build();
54+
}
55+
56+
public Response updateRoutingRules(RoutingRule routingRule)
57+
throws IOException
58+
{
59+
List<RoutingRule> routingRulesList = routingRulesManager.updateRoutingRule(routingRule);
60+
return Response.ok(Result.ok(routingRulesList)).build();
61+
}
62+
}

gateway-ha/src/test/java/io/trino/gateway/ha/router/TestRoutingAPI.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ void testGetRoutingRulesAPI()
7777
{
7878
Request request =
7979
new Request.Builder()
80-
.url("http://localhost:" + routerPort + "/webapp/getRoutingRules")
80+
.url("http://localhost:" + routerPort + "/gateway/routing-rules/all")
8181
.get()
8282
.build();
8383
Response response = httpClient.newCall(request).execute();
@@ -105,7 +105,7 @@ void testUpdateRoutingRulesAPI()
105105
RoutingRule updatedRoutingRules = new RoutingRule("airflow", "if query from airflow, route to adhoc group", 0, List.of("result.put(\"routingGroup\", \"adhoc\")"), "request.getHeader(\"X-Trino-Source\") == \"JDBC\"");
106106
RequestBody requestBody = RequestBody.create(OBJECT_MAPPER.writeValueAsString(updatedRoutingRules), MediaType.parse("application/json; charset=utf-8"));
107107
Request request = new Request.Builder()
108-
.url("http://localhost:" + routerPort + "/webapp/updateRoutingRules")
108+
.url("http://localhost:" + routerPort + "/gateway/routing-rules/modify/update")
109109
.addHeader("Content-Type", "application/json")
110110
.post(requestBody)
111111
.build();
@@ -116,7 +116,7 @@ void testUpdateRoutingRulesAPI()
116116
//Fetch the routing rules to see if the update was successful
117117
Request request2 =
118118
new Request.Builder()
119-
.url("http://localhost:" + routerPort + "/webapp/getRoutingRules")
119+
.url("http://localhost:" + routerPort + "/gateway/routing-rules/all")
120120
.get()
121121
.build();
122122
Response response2 = httpClient.newCall(request2).execute();
@@ -139,13 +139,26 @@ void testUpdateRoutingRulesAPI()
139139
RoutingRule revertRoutingRules = new RoutingRule("airflow", "if query from airflow, route to etl group", 0, List.of("result.put(\"routingGroup\", \"etl\")"), "request.getHeader(\"X-Trino-Source\") == \"airflow\"");
140140
RequestBody requestBody3 = RequestBody.create(OBJECT_MAPPER.writeValueAsString(revertRoutingRules), MediaType.parse("application/json; charset=utf-8"));
141141
Request request3 = new Request.Builder()
142-
.url("http://localhost:" + routerPort + "/webapp/updateRoutingRules")
142+
.url("http://localhost:" + routerPort + "/gateway/routing-rules/modify/update")
143143
.addHeader("Content-Type", "application/json")
144144
.post(requestBody3)
145145
.build();
146146
httpClient.newCall(request3).execute();
147147
}
148148

149+
@Test
150+
void testWebappRoutingRulesCompatibilityAPI()
151+
throws Exception
152+
{
153+
Request request =
154+
new Request.Builder()
155+
.url("http://localhost:" + routerPort + "/webapp/getRoutingRules")
156+
.get()
157+
.build();
158+
Response response = httpClient.newCall(request).execute();
159+
assertThat(response.code()).isEqualTo(200);
160+
}
161+
149162
@Test
150163
void testUIConfigurationAPI()
151164
throws Exception

0 commit comments

Comments
 (0)