Skip to content
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

feat: Enhance AI console functions #365

Merged
merged 2 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
fetch-depth: 1

- name: Build Higress Console Package
run: mvn clean package -f ./backend/pom.xml
run: mvn clean package -f ./backend/pom.xml -Dpmd.language=en

- name: Upload Higress Console Package
uses: actions/upload-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion backend/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ fi
if [ -n "$DEV" ]; then
BUILD_ARGS="$BUILD_ARGS -Dapp.build.dev=$DEV"
fi
./mvnw clean package -Dmaven.test.skip=true $BUILD_ARGS
./mvnw clean package -Dmaven.test.skip=true -Dpmd.language=en $BUILD_ARGS
docker build -t higress-console:0.0.1 -f Dockerfile .
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private UserConfigKey() {}
public static final String SYSTEM_INITIALIZED = "system.initialized";
public static final String LOGIN_PAGE_PROMPT_KEY = "login.prompt";
public static final String DASHBOARD_URL = "dashboard.url";
public static final String DASHBOARD_URL_PREFIX = "dashboard.url.";
public static final String CHAT_ENABLED = "chat.enabled";
public static final String ADMIN_PASSWORD_CHANGE_DISABLED = "admin.password-change.disabled";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.higress.console.controller.dto.DashboardInfo;
import com.alibaba.higress.console.controller.dto.DashboardType;
import com.alibaba.higress.console.controller.dto.Response;
import com.alibaba.higress.sdk.exception.ValidationException;
import com.alibaba.higress.console.service.DashboardService;
import com.alibaba.higress.sdk.exception.ValidationException;

/**
* @author CH3CHO
Expand All @@ -48,25 +49,41 @@ public void setDashboardService(DashboardService dashboardService) {
@GetMapping("/init")
public ResponseEntity<Response<DashboardInfo>> init(@RequestParam(required = false) Boolean force) {
dashboardService.initializeDashboard(Boolean.TRUE.equals(force));
return info();
return info(null);
}

@GetMapping("/info")
public ResponseEntity<Response<DashboardInfo>> info() {
return ResponseEntity.ok(Response.success(dashboardService.getDashboardInfo()));
public ResponseEntity<Response<DashboardInfo>> info(@RequestParam(required = false) String type) {
DashboardType dashboardType = toDashboardType(type);
return ResponseEntity.ok(Response.success(dashboardService.getDashboardInfo(dashboardType)));
}

@PutMapping("/info")
public ResponseEntity<Response<DashboardInfo>> setUrl(@RequestBody DashboardInfo dashboardInfo) {
public ResponseEntity<Response<DashboardInfo>> setUrl(@RequestParam(required = false) String type,
@RequestBody DashboardInfo dashboardInfo) {
DashboardType dashboardType = toDashboardType(type);
if (StringUtils.isEmpty(dashboardInfo.getUrl())) {
throw new ValidationException("Missing required parameter: url");
}
dashboardService.setDashboardUrl(dashboardInfo.getUrl());
return info();
dashboardService.setDashboardUrl(dashboardType, dashboardInfo.getUrl());
return info(dashboardType.toString());
}

@GetMapping("/configData")
public ResponseEntity<Response<String>> getConfigData(@RequestParam @NotBlank String dataSourceUid) {
return ResponseEntity.ok(Response.success(dashboardService.buildConfigData(dataSourceUid)));
public ResponseEntity<Response<String>> getConfigData(@RequestParam(required = false) String type,
@RequestParam @NotBlank String dataSourceUid) {
DashboardType dashboardType = toDashboardType(type);
return ResponseEntity.ok(Response.success(dashboardService.buildConfigData(dashboardType, dataSourceUid)));
}

private static DashboardType toDashboardType(String type) {
if (StringUtils.isEmpty(type)) {
return DashboardType.MAIN;
}
try {
return DashboardType.valueOf(type.toUpperCase());
} catch (IllegalArgumentException e) {
throw new ValidationException("Unknown dashboard type: " + type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ private ResponseEntity<Response<WasmPluginInstance>> queryInstance(WasmPluginIns
String name) {
WasmPluginInstance instance = wasmPluginInstanceService.query(scope, target, name, false);
if (instance == null) {
instance = WasmPluginInstance.builder().scope(scope).target(target).pluginName(name).internal(false)
.enabled(false).build();
instance = WasmPluginInstance.builder().pluginName(name).internal(false).enabled(false).build();
instance.setTarget(scope, target);
}
return ControllerUtil.buildResponseEntity(instance);
}
Expand All @@ -234,8 +234,7 @@ private ResponseEntity<Response<WasmPluginInstance>> addOrUpdateInstance(WasmPlu
if (plugin == null) {
throw new ValidationException("Unsupported plugin: " + name);
}
instance.setScope(scope);
instance.setTarget(target);
instance.setTarget(scope, target);
instance = wasmPluginInstanceService.addOrUpdate(instance);
return ControllerUtil.buildResponseEntity(instance);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2022-2024 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.higress.console.controller.dto;

public enum DashboardType {

/**
* Main dashboard
*/
MAIN,
/**
* AI Gateway dashboard
*/
AI,
/**
* Access log dashboard
*/
LOG
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,29 @@
import javax.servlet.http.HttpServletResponse;

import com.alibaba.higress.console.controller.dto.DashboardInfo;
import com.alibaba.higress.console.controller.dto.DashboardType;

/**
* @author CH3CHO
*/
public interface DashboardService {

@Deprecated
DashboardInfo getDashboardInfo();

DashboardInfo getDashboardInfo(DashboardType type);

void initializeDashboard(boolean overwrite);

@Deprecated
void setDashboardUrl(String url);

void setDashboardUrl(DashboardType type, String url);

@Deprecated
String buildConfigData(String dataSourceUid);

String buildConfigData(DashboardType type, String dataSourceUid);

void forwardDashboardRequest(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -46,6 +49,7 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.tomcat.util.http.fileupload.util.Streams;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

Expand All @@ -58,6 +62,7 @@
import com.alibaba.higress.console.constant.SystemConfigKey;
import com.alibaba.higress.console.constant.UserConfigKey;
import com.alibaba.higress.console.controller.dto.DashboardInfo;
import com.alibaba.higress.console.controller.dto.DashboardType;
import com.alibaba.higress.sdk.exception.BusinessException;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
Expand All @@ -79,6 +84,7 @@ public class DashboardServiceImpl implements DashboardService {
private static final String DATASOURCE_UID_PLACEHOLDER = "${datasource.id}";
private static final String MAIN_DASHBOARD_DATA_PATH = "/dashboard/main.json";
private static final String LOG_DASHBOARD_DATA_PATH = "/dashboard/logs.json";
private static final String AI_DASHBOARD_DATA_PATH = "/dashboard/ai.json";
private static final String PROM_DATASOURCE_TYPE = "prometheus";
private static final String LOKI_DATASOURCE_TYPE = "loki";
private static final String DATASOURCE_ACCESS = "proxy";
Expand Down Expand Up @@ -129,10 +135,7 @@ public class DashboardServiceImpl implements DashboardService {
private CloseableHttpClient realServerClient;
private String realServerBaseUrl;

private String mainDashboardConfiguration;
private GrafanaDashboard configuredMainDashboard;
private String logDashboardConfiguration;
private GrafanaDashboard configuredLogDashboard;
private Map<DashboardType, DashboardConfiguration> dashboardConfigurations;

@Resource
public void setConfigService(ConfigService configService) {
Expand All @@ -141,14 +144,18 @@ public void setConfigService(ConfigService configService) {

@PostConstruct
public void initialize() {
Map<DashboardType, DashboardConfiguration> dashboardConfigurations = new HashMap<>();
try {
mainDashboardConfiguration = IOUtils.resourceToString(MAIN_DASHBOARD_DATA_PATH, StandardCharsets.UTF_8);
configuredMainDashboard = GrafanaClient.parseDashboardData(mainDashboardConfiguration);
logDashboardConfiguration = IOUtils.resourceToString(LOG_DASHBOARD_DATA_PATH, StandardCharsets.UTF_8);
configuredLogDashboard = GrafanaClient.parseDashboardData(logDashboardConfiguration);
dashboardConfigurations.put(DashboardType.MAIN,
new DashboardConfiguration(DashboardType.MAIN, MAIN_DASHBOARD_DATA_PATH));
dashboardConfigurations.put(DashboardType.AI,
new DashboardConfiguration(DashboardType.AI, AI_DASHBOARD_DATA_PATH));
dashboardConfigurations.put(DashboardType.LOG,
new DashboardConfiguration(DashboardType.LOG, LOG_DASHBOARD_DATA_PATH));
} catch (IOException e) {
throw new IllegalStateException("Error occurs when loading dashboard configurations from resource.", e);
}
this.dashboardConfigurations = Collections.unmodifiableMap(dashboardConfigurations);

if (isBuiltIn()) {
try {
Expand All @@ -170,7 +177,12 @@ public void initialize() {

@Override
public DashboardInfo getDashboardInfo() {
return isBuiltIn() ? getBuiltInDashboardInfo() : getConfiguredDashboardInfo();
return getDashboardInfo(DashboardType.MAIN);
}

@Override
public DashboardInfo getDashboardInfo(DashboardType type) {
return isBuiltIn() ? getBuiltInDashboardInfo(type) : getConfiguredDashboardInfo(type);
}

@Override
Expand All @@ -194,26 +206,39 @@ public void initializeDashboard(boolean overwrite) {
} catch (IOException e) {
throw new BusinessException("Error occurs when loading dashboard info from Grafana.", e);
}
configureDashboard(results, configuredMainDashboard.getTitle(), mainDashboardConfiguration, promDatasourceUid,
overwrite);
configureDashboard(results, configuredLogDashboard.getTitle(), logDashboardConfiguration, lokiDatasourceUid,
overwrite);
for (DashboardConfiguration configuration : dashboardConfigurations.values()) {
String datasourceId = configuration.getType() == DashboardType.LOG ? lokiDatasourceUid : promDatasourceUid;
configureDashboard(results, configuration.getDashboard().getTitle(), configuration.getRaw(), datasourceId,
overwrite);
}
}

@Override
public void setDashboardUrl(String url) {
setDashboardUrl(DashboardType.MAIN, url);
}

@Override
public void setDashboardUrl(DashboardType type, String url) {
if (StringUtils.isBlank(url)) {
throw new IllegalArgumentException("url cannot be null or blank.");
}
if (isBuiltIn()) {
throw new IllegalStateException("Manual dashboard configuration is disabled.");
}
configService.setConfig(UserConfigKey.DASHBOARD_URL, url);
DashboardConfiguration configuration = getDashboardConfiguration(type);
configService.setConfig(configuration.getConfigKey(), url);
}

@Override
public String buildConfigData(String datasourceUid) {
return buildConfigData(mainDashboardConfiguration, datasourceUid);
return buildConfigData(DashboardType.MAIN, datasourceUid);
}

@Override
public String buildConfigData(DashboardType type, String datasourceUid) {
DashboardConfiguration configuration = getDashboardConfiguration(type);
return buildConfigData(configuration.getRaw(), datasourceUid);
}

@Override
Expand Down Expand Up @@ -323,7 +348,11 @@ private void configureDashboard(List<GrafanaSearchResult> results, String title,
}
}

private DashboardInfo getBuiltInDashboardInfo() {
private DashboardInfo getBuiltInDashboardInfo(DashboardType type) {
DashboardConfiguration configuration = dashboardConfigurations.get(type);
if (configuration == null) {
throw new IllegalArgumentException("Invalid dashboard type: " + type);
}
List<GrafanaSearchResult> results;
try {
results = grafanaClient.search(null, SearchType.DB, null, null);
Expand All @@ -333,7 +362,7 @@ private DashboardInfo getBuiltInDashboardInfo() {
if (CollectionUtils.isEmpty(results)) {
return new DashboardInfo(true, null, null);
}
String expectedTitle = configuredMainDashboard.getTitle();
String expectedTitle = configuration.getDashboard().getTitle();
if (StringUtils.isEmpty(expectedTitle)) {
throw new IllegalStateException("No title is found in the configured dashboard.");
}
Expand All @@ -342,8 +371,9 @@ private DashboardInfo getBuiltInDashboardInfo() {
return result.map(r -> new DashboardInfo(true, r.getUid(), r.getUrl())).orElse(null);
}

private DashboardInfo getConfiguredDashboardInfo() {
String url = configService.getString(UserConfigKey.DASHBOARD_URL);
private DashboardInfo getConfiguredDashboardInfo(DashboardType type) {
DashboardConfiguration configuration = dashboardConfigurations.get(type);
String url = configService.getString(configuration.getConfigKey());
return new DashboardInfo(false, null, url);
}

Expand Down Expand Up @@ -411,4 +441,31 @@ public void run() {
}
}
}

private @NotNull DashboardConfiguration getDashboardConfiguration(DashboardType type) {
DashboardConfiguration configuration = dashboardConfigurations.get(type);
if (configuration == null) {
throw new IllegalArgumentException("Invalid dashboard type: " + type);
}
return configuration;
}

@lombok.Value
private static class DashboardConfiguration {

DashboardType type;
String configKey;
String resourcePath;
String raw;
GrafanaDashboard dashboard;

public DashboardConfiguration(DashboardType type, String resourcePath) throws IOException {
this.type = type;
this.configKey = type == DashboardType.MAIN ? UserConfigKey.DASHBOARD_URL
: UserConfigKey.DASHBOARD_URL_PREFIX + type.toString().toLowerCase(Locale.ROOT);
this.resourcePath = resourcePath;
this.raw = IOUtils.resourceToString(resourcePath, StandardCharsets.UTF_8);
this.dashboard = GrafanaClient.parseDashboardData(this.raw);
}
}
}
Loading
Loading