Skip to content

Commit

Permalink
Show and modify routing rules from the UI
Browse files Browse the repository at this point in the history
Remove console logs and renamed api

Add api documentation and changes related to PR comments
  • Loading branch information
prakhar10 committed Sep 9, 2024
1 parent 56e33c5 commit d3c7855
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 22 deletions.
13 changes: 13 additions & 0 deletions docs/gateway-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

This API can be used to programmatically update the Routing Rules.
Rule will be updated based on the rule name.
```shell
curl -X POST http://localhost:8080/webapp/updateRoutingRules \
-d '{ "name": "trino-rule",
"description": "updated rule description",
"priority": 0,
"actions": ["updated action"],
"condition": "updated condition"
}'
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.gateway.ha.domain;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

/**
* RoutingRules
*
* @param name name of the routing rule
* @param description description of the routing rule
* @param priority priority of the routing rule
* @param actions actions of the routing rule
* @param condition condition of the routing rule
*/
public record RoutingRules(
@JsonProperty("name") String name,
@JsonProperty("description") String description,
@JsonProperty("priority") Integer priority,
@JsonProperty("actions") List<String> actions,
@JsonProperty("condition") String condition)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@
*/
package io.trino.gateway.ha.resource;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.trino.gateway.ha.clustermonitor.ClusterStats;
import io.trino.gateway.ha.config.HaGatewayConfiguration;
import io.trino.gateway.ha.config.ProxyBackendConfiguration;
import io.trino.gateway.ha.domain.Result;
import io.trino.gateway.ha.domain.RoutingRules;
import io.trino.gateway.ha.domain.TableData;
import io.trino.gateway.ha.domain.request.GlobalPropertyRequest;
import io.trino.gateway.ha.domain.request.QueryDistributionRequest;
Expand All @@ -36,6 +43,7 @@
import io.trino.gateway.ha.router.ResourceGroupsManager;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
Expand All @@ -44,11 +52,15 @@
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand All @@ -59,6 +71,7 @@
import static java.util.Objects.requireNonNullElse;

@Path("/webapp")
@Singleton
public class GatewayWebAppResource
{
private static final LocalDateTime START_TIME = LocalDateTime.now();
Expand All @@ -67,18 +80,21 @@ public class GatewayWebAppResource
private final QueryHistoryManager queryHistoryManager;
private final BackendStateManager backendStateManager;
private final ResourceGroupsManager resourceGroupsManager;
private final HaGatewayConfiguration configuration;

@Inject
public GatewayWebAppResource(
GatewayBackendManager gatewayBackendManager,
QueryHistoryManager queryHistoryManager,
BackendStateManager backendStateManager,
ResourceGroupsManager resourceGroupsManager)
ResourceGroupsManager resourceGroupsManager,
HaGatewayConfiguration configuration)
{
this.gatewayBackendManager = requireNonNull(gatewayBackendManager, "gatewayBackendManager is null");
this.queryHistoryManager = requireNonNull(queryHistoryManager, "queryHistoryManager is null");
this.backendStateManager = requireNonNull(backendStateManager, "backendStateManager is null");
this.resourceGroupsManager = requireNonNull(resourceGroupsManager, "resourceGroupsManager is null");
this.configuration = requireNonNull(configuration, "configuration is null");
}

@POST
Expand Down Expand Up @@ -423,4 +439,62 @@ public Response readExactMatchSourceSelector()
List<ResourceGroupsManager.ExactSelectorsDetail> selectorsDetailList = resourceGroupsManager.readExactMatchSourceSelector();
return Response.ok(Result.ok(selectorsDetailList)).build();
}

@GET
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_JSON)
@Path("/getRoutingRules")
public Response getRoutingRules()
{
try {
String rulesConfigPath = configuration.getRoutingRules().getRulesConfigPath();
YAMLFactory yamlFactory = new YAMLFactory();
ObjectMapper yamlReader = new ObjectMapper(yamlFactory);
YAMLParser yamlParser = yamlFactory.createParser(new String(Files.readAllBytes(Paths.get(rulesConfigPath))));
List<RoutingRules> routingRulesList = yamlReader
.readValues(yamlParser, new TypeReference<RoutingRules>() {})
.readAll();
return Response.ok(Result.ok(routingRulesList)).build();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}

@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("/updateRoutingRules")
public synchronized Response updateRoutingRules(RoutingRules routingRules)
{
String rulesConfigPath = configuration.getRoutingRules().getRulesConfigPath();
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
List<RoutingRules> routingRulesList = new ArrayList<>();
YAMLFactory yamlFactory = new YAMLFactory();
try {
YAMLParser yamlParser = yamlFactory.createParser(new String(Files.readAllBytes(Paths.get(rulesConfigPath))));
routingRulesList = yamlReader
.readValues(yamlParser, new TypeReference<RoutingRules>() {})
.readAll();

for (int i = 0; i < routingRulesList.size(); i++) {
if (routingRulesList.get(i).name().equals(routingRules.name())) {
routingRulesList.set(i, routingRules);
break;
}
}

ObjectMapper yamlWriter = new ObjectMapper(new YAMLFactory());
StringBuilder yamlContent = new StringBuilder();
for (RoutingRules rule : routingRulesList) {
yamlContent.append(yamlWriter.writeValueAsString(rule));
}
Files.write(Paths.get(rulesConfigPath), yamlContent.toString().getBytes());
}
catch (IOException e) {
throw new RuntimeException(e);
}
return Response.ok(Result.ok(routingRulesList)).build();
}
}
11 changes: 11 additions & 0 deletions webapp/src/api/webapp/routing-rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {api} from "../base";
import {RoutingRulesData} from "../../types/routing-rules";

export async function routingRulesApi(): Promise<any> {
const response = await api.get('/webapp/getRoutingRules');
return response;
}

export async function updateRoutingRulesApi(body: Record<string, any>): Promise<RoutingRulesData> {
return api.post('/webapp/updateRoutingRules', body)
}
23 changes: 14 additions & 9 deletions webapp/src/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Nav, Avatar, Layout, Dropdown, Button, Toast, Modal, Tag } from '@douyinfe/semi-ui';
import { IconGithubLogo, IconDoubleChevronRight, IconDoubleChevronLeft, IconMoon, IconSun, IconMark, IconIdCard } from '@douyinfe/semi-icons';
import { IconGithubLogo, IconDoubleChevronRight, IconDoubleChevronLeft, IconMoon, IconSun, IconMark, IconIdCard, IconUserSetting, IconUser } from '@douyinfe/semi-icons';
import styles from './layout.module.scss';
import { useEffect, useState } from 'react';
import { Link, useLocation } from "react-router-dom";
Expand Down Expand Up @@ -87,14 +87,19 @@ export const RootLayout = (props: {
</Dropdown.Menu>
}
>
<Avatar
size="small"
src={access.avatar || config.avatar}
color="blue"
className={styles.avatar}
>
{access.nickName}
</Avatar>
{access.roles.includes('ADMIN') ? (
<Button icon={<IconUserSetting
size="extra-large"
color="orange"
className={styles.semiIconsBell} />}>
</Button>
) : (
<Button icon={<IconUser
size="extra-large"
color="blue"
className={styles.semiIconsBell} />}>
</Button>
)}
</Dropdown>
</div>
}
Expand Down
Loading

0 comments on commit d3c7855

Please sign in to comment.