-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show and modify routing rules from the UI #433
base: main
Are you sure you want to change the base?
Conversation
gateway-ha/src/main/java/io/trino/gateway/ha/resource/GatewayWebAppResource.java
Show resolved
Hide resolved
gateway-ha/src/main/java/io/trino/gateway/ha/resource/GatewayWebAppResource.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a nice improvement, thank you! I reviewed the Java half, and will try to find someone for the typescript.
My biggest concern here is not with the code, but with allowing users to supply input that will be written to disk on the server, then executed by the jeasy
rules engine. We should take a close look at jeasy
and mvel
to understand what security vulnerabilities we might be introducing. At minimum we should log all of the updates so that they can be audited. @wendigo and @ebyhr wdyt?
* @param actions actions of the routing rule | ||
* @param condition condition of the routing rule | ||
*/ | ||
@JsonIgnoreProperties(ignoreUnknown = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ignore unknown properties? If a user supplies an invalid or misspelled property we should notify them by throwing an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, i'll fix that!
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory()); | ||
YAMLParser parser = new YAMLFactory().createParser(content); | ||
List<RoutingRules> routingRulesList = new ArrayList<>(); | ||
while (parser.nextToken() != null) { | ||
RoutingRules routingRules = yamlReader.readValue(parser, RoutingRules.class); | ||
routingRulesList.add(routingRules); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
List<RoutingRulesRecord> routingRulesList = yamlReader.readValue(new File(rulesConfigPath), new TypeReference<List<RoutingRulesRecord>>() {});
work here? Same comment on the other yaml parsing below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, let me try.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this but it does not seem to work. Looks like if we use this typereference code it wants the yaml file in a certain format to correctly deserialize it into an object, something like -
- name: "airflow"
description: "if query from airflow, route to etl group"
priority: 0
condition: 'request.getHeader("X-Trino-Source") == "airflow"'
actions:
- 'result.put("routingGroup", "etl")'
But jeasy
rules engine is not able to read this format when we try to fetch the rules. So it doesn't seem to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, I forgot jeasy
concatenates multiple documents instead of having a list of rules. I think
YAMLFactory yamlFactory = new YAMLFactory();
ObjectMapper yamlReader = new ObjectMapper(yamlFactory);
YAMLParser yamlParser = yamlFactory.createParser(rulesConfigPath)
List<ObjectNode> docs = yamlReader
.readValues(yamlParser, new TypeReference<RoutingRulesRecord>() {})
.readAll();
should work (ref).
gateway-ha/src/main/java/io/trino/gateway/ha/resource/GatewayWebAppResource.java
Show resolved
Hide resolved
for (int i = 0; i < routingRulesList.size(); i++) { | ||
if (routingRulesList.get(i).name().equals(routingRules.name())) { | ||
routingRulesList.set(i, routingRules); | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as the name of this method suggests, this updates an existing rule. It does not appear that this change adds support for deleting an existing rule or adding a new one. I guess this may be due to the UI changes required, but would it be difficult to add this functionality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it would be difficult. I thought of initially going with only updates and in a separate PR i will put Add and Delete functionality. Will that be ok? Or should I add it in this PR itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that plan sounds fine to me, thanks!
This functionality will be accessible only for users with ADMIN privileges. Regular users will have this as Read-Only. I also have a functionality implemented in our internal repo where we are auditing changes being made to the Clusters from the UI. I can raise a PR for that as well and will add functionality to audit routing rules as well. I was thinking of getting this reviewed first and then from comments and suggestions I will raise the Audit logs PR. |
@Consumes(MediaType.APPLICATION_JSON) | ||
@Produces(MediaType.APPLICATION_JSON) | ||
@Path("/updateRoutingRules") | ||
public Response updateRoutingRules(RoutingRules routingRules) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following sequence would result in user 1's update being lost:
- user 1 calls
updateRoutingRules
to update rule 1 - user 2 calls
updateRoutingRules
to update rule 2, which reads the rules file BEFORE user 1's update finishes writing - user 1's call to
updateRoutingRules
completes and writes new rules out - user 2's call to
updateRoutingRules
completes and writes new rules out
I think this issue could be avoided by adding synchronized
to the method and making the method static or marking the class @Singleton
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is looking good, we're almost there. @mosabua and I discussed the need to support this for clustered deployments. We should also provide a way to disable this feature in case someone doesn't want to bother setting up a shared filesystem
@@ -91,3 +91,16 @@ Will return a JSON array of active Trino cluster backends: | |||
curl -X POST http://localhost:8080/gateway/backend/activate/trino-2 | |||
``` | |||
|
|||
## Update Routing Rules |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add some additional documentation on how to set up your infrastructure when using this feature with a clustered deployment. If the file is only updated on the trino gateway instance that handles the API call, then routing rules will get out of sync with one another. Therefore the rules must be stored on a shared filesystem. Please test and document this scenario.
This emphasizes that we should really move the rules to the DB, but that will be future work.
@prakhar10 please squash these commits |
So this means provide an option or a config to show/disable the Routing Rules option in the left panel of the UI right? |
I think for now we can just say in the docs that you must provide shared storage for that feature to work and in terms of disabling it could be just a config option option .. I think over time we will need various config options for the UI anyway |
Remove console logs and renamed api Add api documentation and changes related to PR comments
85287b9
to
d3c7855
Compare
Description
Additional context and related issues
Release notes
( ) This is not user-visible or is docs only, and no release notes are required.
(X) Release notes are required. Please propose a release note for me.
( ) Release notes are required, with the following suggested text:
*