/* * Copyright 1999-2018 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.csp.sentinel.dashboard.service; import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.discovery.AppInfo; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterGroupEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterClientModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.request.ClusterServerModifyRequest; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterClientStateVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStatePairVO; import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterUniversalStateVO; import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.dashboard.util.ClusterEntityUtils; import com.alibaba.csp.sentinel.util.StringUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; /** * @author Eric Zhao * @since 1.4.0 */ @Service public class ClusterConfigService { @Autowired private SentinelApiClient sentinelApiClient; @Autowired private AppManagement appManagement; public CompletableFuture modifyClusterClientConfig(ClusterClientModifyRequest request) { if (notClientRequestValid(request)) { throw new IllegalArgumentException("Invalid request"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterClientConfig(app, ip, port, request.getClientConfig()) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_CLIENT)); } private boolean notClientRequestValid(/* @NonNull */ ClusterClientModifyRequest request) { ClusterClientConfig config = request.getClientConfig(); return config == null || StringUtil.isEmpty(config.getServerHost()) || config.getServerPort() == null || config.getServerPort() <= 0 || config.getRequestTimeout() == null || config.getRequestTimeout() <= 0; } public CompletableFuture modifyClusterServerConfig(ClusterServerModifyRequest request) { ServerTransportConfig transportConfig = request.getTransportConfig(); ServerFlowConfig flowConfig = request.getFlowConfig(); Set namespaceSet = request.getNamespaceSet(); if (invalidTransportConfig(transportConfig)) { throw new IllegalArgumentException("Invalid transport config in request"); } if (invalidFlowConfig(flowConfig)) { throw new IllegalArgumentException("Invalid flow config in request"); } if (namespaceSet == null) { throw new IllegalArgumentException("namespace set cannot be null"); } String app = request.getApp(); String ip = request.getIp(); int port = request.getPort(); return sentinelApiClient.modifyClusterServerNamespaceSet(app, ip, port, namespaceSet) .thenCompose(v -> sentinelApiClient.modifyClusterServerTransportConfig(app, ip, port, transportConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterServerFlowConfig(app, ip, port, flowConfig)) .thenCompose(v -> sentinelApiClient.modifyClusterMode(ip, port, ClusterStateManager.CLUSTER_SERVER)); } /** * Get cluster state list of all available machines of provided application. * @param app application name * @return cluster state list of all available machines of the application * @since 1.4.1 */ public CompletableFuture> getClusterUniversalState(String app) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return CompletableFuture.completedFuture(new ArrayList<>()); } List> futures = appInfo.getMachines() .stream() .filter(e -> e.isHealthy()) .map(machine -> getClusterUniversalState(app, machine.getIp(), machine.getPort()) .thenApply(e -> new ClusterUniversalStatePairVO(machine.getIp(), machine.getPort(), e))) .collect(Collectors.toList()); return AsyncUtils.sequenceSuccessFuture(futures); } public CompletableFuture getClusterUniversalStateForAppMachine(String app, String machineId) { if (StringUtil.isBlank(app)) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app cannot be empty")); } AppInfo appInfo = appManagement.getDetailApp(app); if (appInfo == null || appInfo.getMachines() == null) { return AsyncUtils.newFailedFuture(new IllegalArgumentException("app does not have machines")); } boolean machineOk = appInfo.getMachines() .stream() .filter(e -> e.isHealthy()) .map(e -> e.getIp() + '@' + e.getPort()) .anyMatch(e -> e.equals(machineId)); if (!machineOk) { return AsyncUtils.newFailedFuture(new IllegalStateException("machine does not exist or disconnected")); } return getClusterUniversalState(app).thenApply(ClusterEntityUtils::wrapToClusterGroup) .thenCompose(e -> e.stream() .filter(e1 -> e1.getMachineId().equals(machineId)) .findAny() .map(CompletableFuture::completedFuture) .orElse(AsyncUtils.newFailedFuture(new IllegalStateException("not a server: " + machineId)))); } public CompletableFuture getClusterUniversalState(String app, String ip, int port) { return sentinelApiClient.fetchClusterMode(ip, port) .thenApply(e -> new ClusterUniversalStateVO().setStateInfo(e)) .thenCompose(vo -> { if (vo.getStateInfo().getClientAvailable()) { return sentinelApiClient.fetchClusterClientInfoAndConfig(ip, port) .thenApply(cc -> vo.setClient(new ClusterClientStateVO().setClientConfig(cc))); } else { return CompletableFuture.completedFuture(vo); } }) .thenCompose(vo -> { if (vo.getStateInfo().getServerAvailable()) { return sentinelApiClient.fetchClusterServerBasicInfo(ip, port).thenApply(vo::setServer); } else { return CompletableFuture.completedFuture(vo); } }); } private boolean invalidTransportConfig(ServerTransportConfig transportConfig) { return transportConfig == null || transportConfig.getPort() == null || transportConfig.getPort() <= 0 || transportConfig.getIdleSeconds() == null || transportConfig.getIdleSeconds() <= 0; } private boolean invalidFlowConfig(ServerFlowConfig flowConfig) { return flowConfig == null || flowConfig.getSampleCount() == null || flowConfig.getSampleCount() <= 0 || flowConfig.getIntervalMs() == null || flowConfig.getIntervalMs() <= 0 || flowConfig.getIntervalMs() % flowConfig.getSampleCount() != 0 || flowConfig.getMaxAllowedQps() == null || flowConfig.getMaxAllowedQps() < 0; } }