diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/CommonConstant.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/CommonConstant.java index 7533d49b22..f3e41745c8 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/CommonConstant.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/CommonConstant.java @@ -127,6 +127,16 @@ public class CommonConstant { */ public static final String ARTIFACT_NAME_KEY = "artifact"; + /** + * ~ string + */ + public static final String WAVY_LINE = "~"; + + /** + * | string after escaping + */ + public static final String ESCAPED_VERTICAL_LINE = "\\|"; + /** * The key of agent path */ diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java index 39f78b45ac..339e1ec108 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsCoreService.java @@ -45,4 +45,11 @@ public interface XdsCoreService extends BaseService { * @return XdsRoute */ XdsLoadBalanceService getLoadBalanceService(); + + /** + * get XDSFlowControlService + * + * @return XdsFlowControlService + */ + XdsFlowControlService getXdsFlowControlService(); } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsFlowControlService.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsFlowControlService.java new file mode 100644 index 0000000000..876365d5c5 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/XdsFlowControlService.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.core.service.xds; + +import io.sermant.core.service.xds.entity.XdsHttpFault; +import io.sermant.core.service.xds.entity.XdsInstanceCircuitBreakers; +import io.sermant.core.service.xds.entity.XdsRateLimit; +import io.sermant.core.service.xds.entity.XdsRequestCircuitBreakers; +import io.sermant.core.service.xds.entity.XdsRetryPolicy; + +import java.util.Optional; + +/** + * xDS FlowControl service + * + * @author zhp + * @since 2024-11-27 + **/ +public interface XdsFlowControlService { + /** + * get Circuit breaker information for the client's request, When the number of active requests for an + * instance reaches the specified limit, it will trigger a circuit breaker + * + * @param serviceName service name + * @param clusterName cluster name + * @return circuit breaker rules + */ + Optional getRequestCircuitBreakers(String serviceName, String clusterName); + + /** + * get Circuit breaker information of server instance, The instance has reached the specified number of errors + * and will trigger a circuit breaker and the instance will be removed for a period of time + * + * @param serviceName service name + * @param clusterName cluster name + * @return Outlier Detection rules + */ + Optional getInstanceCircuitBreakers(String serviceName, String clusterName); + + /** + * get retry policy of route name + * + * @param serviceName service name + * @param routeName route name + * @return retry policy + */ + Optional getRetryPolicy(String serviceName, String routeName); + + /** + * get rate limit of route name + * + * @param serviceName service name + * @param routeName route name + * @param port port + * @return rate limit rule + */ + Optional getRateLimit(String serviceName, String routeName, String port); + + /** + * get http fault of route name + * + * @param serviceName service name + * @param routeName route name + * @return http fault rule + */ + Optional getHttpFault(String serviceName, String routeName); +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsAbort.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsAbort.java index 013340dea7..ceb76e7cdd 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsAbort.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsAbort.java @@ -31,7 +31,7 @@ public class XdsAbort { /** * The percentage of requests/ operations/ connections that will be aborted with the error code */ - private float percentage; + private int percentage; public int getHttpStatus() { return httpStatus; @@ -41,11 +41,11 @@ public void setHttpStatus(int httpStatus) { this.httpStatus = httpStatus; } - public float getPercentage() { + public int getPercentage() { return percentage; } - public void setPercentage(float percentage) { + public void setPercentage(int percentage) { this.percentage = percentage; } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCluster.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCluster.java index 53abe91e3e..9977c24443 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCluster.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCluster.java @@ -31,6 +31,10 @@ public class XdsCluster { private boolean isLocalityLb; + private XdsRequestCircuitBreakers requestCircuitBreakers; + + private XdsInstanceCircuitBreakers instanceCircuitBreakers; + public String getClusterName() { return clusterName; } @@ -62,4 +66,20 @@ public boolean isLocalityLb() { public void setLocalityLb(boolean localityLb) { isLocalityLb = localityLb; } + + public XdsRequestCircuitBreakers getRequestCircuitBreakers() { + return requestCircuitBreakers; + } + + public void setRequestCircuitBreakers(XdsRequestCircuitBreakers requestCircuitBreakers) { + this.requestCircuitBreakers = requestCircuitBreakers; + } + + public XdsInstanceCircuitBreakers getInstanceCircuitBreakers() { + return instanceCircuitBreakers; + } + + public void setInstanceCircuitBreakers(XdsInstanceCircuitBreakers instanceCircuitBreakers) { + this.instanceCircuitBreakers = instanceCircuitBreakers; + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsDelay.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsDelay.java index 803bae277a..3d63f923d9 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsDelay.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsDelay.java @@ -31,7 +31,7 @@ public class XdsDelay { /** * The percentage of requests on which the delay will be injected */ - private float percentage; + private int percentage; public long getFixedDelay() { return fixedDelay; @@ -41,11 +41,11 @@ public void setFixedDelay(long fixedDelay) { this.fixedDelay = fixedDelay; } - public float getPercentage() { + public int getPercentage() { return percentage; } - public void setPercentage(float percentage) { + public void setPercentage(int percentage) { this.percentage = percentage; } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsOutlierDetection.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakers.java similarity index 79% rename from sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsOutlierDetection.java rename to sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakers.java index 450147802e..df9543e85d 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsOutlierDetection.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakers.java @@ -17,12 +17,13 @@ package io.sermant.core.service.xds.entity; /** - * Xds Outlier Detection information + * Circuit breaker information of server instance, The instance has reached the specified number of errors and will + * trigger a circuit breaker and the instance will be removed for a period of time * * @author zhp * @since 2024-11-18 */ -public class XdsOutlierDetection { +public class XdsInstanceCircuitBreakers { /** * Whether to distinguish between local source failures and external errors. When set to true, * it will detect local source failures. @@ -61,12 +62,18 @@ public class XdsOutlierDetection { /** * The maximum % of an upstream cluster that can be ejected due to outlier detection */ - private float maxEjectionPercent; + private int maxEjectionPercent; /** * The minimum number of hosts in a cluster in order to perform failure percentage-based ejection */ - private float failurePercentageMinimumHosts; + private int failurePercentageMinimumHosts; + + /** + * Outlier detection will be enabled as long as the associated load balancing pool has at least min_health_percent + * hosts in healthy mode. + */ + private double minHealthPercent; public boolean isSplitExternalLocalOriginErrors() { return splitExternalLocalOriginErrors; @@ -116,19 +123,27 @@ public void setBaseEjectionTime(long baseEjectionTime) { this.baseEjectionTime = baseEjectionTime; } - public float getMaxEjectionPercent() { + public int getMaxEjectionPercent() { return maxEjectionPercent; } - public void setMaxEjectionPercent(float maxEjectionPercent) { + public void setMaxEjectionPercent(int maxEjectionPercent) { this.maxEjectionPercent = maxEjectionPercent; } - public float getFailurePercentageMinimumHosts() { + public int getFailurePercentageMinimumHosts() { return failurePercentageMinimumHosts; } - public void setFailurePercentageMinimumHosts(float failurePercentageMinimumHosts) { + public void setFailurePercentageMinimumHosts(int failurePercentageMinimumHosts) { this.failurePercentageMinimumHosts = failurePercentageMinimumHosts; } + + public double getMinHealthPercent() { + return minHealthPercent; + } + + public void setMinHealthPercent(double minHealthPercent) { + this.minHealthPercent = minHealthPercent; + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRateLimit.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRateLimit.java index a5d8848f50..e4ef3d1604 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRateLimit.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRateLimit.java @@ -20,6 +20,12 @@ /** * Xds rate-limiting configuration information + * LocalRateLimit is published through + * EnvoyFilter, + * with routeConfiguration.vhost.name corresponding to the name after + * filling in spec.hosts in + * VirtualService, and + * routeConfiguration.vhost.route.name corresponding to the name in spec.http * * @author zhp * @since 2024-11-18 diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCircuitBreakers.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakers.java similarity index 82% rename from sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCircuitBreakers.java rename to sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakers.java index b1e242fc61..b89dce8b77 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsCircuitBreakers.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakers.java @@ -17,12 +17,13 @@ package io.sermant.core.service.xds.entity; /** - * Xds circuit breaker information + * Circuit breaker information for the client's request, When the number of active requests for an instance + * reaches the specified limit, it will trigger a circuit breaker * * @author zhp * @since 2024-11-18 */ -public class XdsCircuitBreakers { +public class XdsRequestCircuitBreakers { /** * Maximum active request count */ diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRoute.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRoute.java index 41ca560a02..77993555ab 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRoute.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRoute.java @@ -29,6 +29,10 @@ public class XdsRoute { private XdsRouteAction routeAction; + private XdsHttpFault httpFault; + + private XdsRateLimit rateLimit; + public String getName() { return name; } @@ -52,4 +56,20 @@ public XdsRouteAction getRouteAction() { public void setRouteAction(XdsRouteAction routeAction) { this.routeAction = routeAction; } + + public XdsHttpFault getHttpFault() { + return httpFault; + } + + public void setHttpFault(XdsHttpFault httpFault) { + this.httpFault = httpFault; + } + + public XdsRateLimit getRateLimit() { + return rateLimit; + } + + public void setRateLimit(XdsRateLimit rateLimit) { + this.rateLimit = rateLimit; + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRouteAction.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRouteAction.java index eb44873448..a9e7486fb2 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRouteAction.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsRouteAction.java @@ -31,6 +31,8 @@ public class XdsRouteAction { private XdsWeightedClusters weightedClusters; + private XdsRetryPolicy retryPolicy; + public String getCluster() { return cluster; } @@ -55,6 +57,14 @@ public void setWeightedClusters(XdsWeightedClusters weightedClusters) { this.weightedClusters = weightedClusters; } + public XdsRetryPolicy getRetryPolicy() { + return retryPolicy; + } + + public void setRetryPolicy(XdsRetryPolicy retryPolicy) { + this.retryPolicy = retryPolicy; + } + /** * xDS WeightedClusters * diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsServiceCluster.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsServiceCluster.java index d072fb99d5..342d116380 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsServiceCluster.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/xds/entity/XdsServiceCluster.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -104,4 +105,38 @@ public XdsLbPolicy getBaseLbPolicyOfService() { } return xdsCluster.getLbPolicy(); } + + /** + * get XdsRequestCircuitBreakers + * + * @param clusterName cluster name + * @return XdsOutlierDetection + */ + public Optional getRequestCircuitBreakersOfCluster(String clusterName) { + if (clusters == null) { + return Optional.empty(); + } + XdsCluster xdsCluster = clusters.get(clusterName); + if (xdsCluster == null) { + return Optional.empty(); + } + return Optional.ofNullable(xdsCluster.getRequestCircuitBreakers()); + } + + /** + * get XdsInstanceCircuitBreakers + * + * @param clusterName cluster name + * @return XdsOutlierDetection + */ + public Optional getInstanceCircuitBreakersOfCluster(String clusterName) { + if (clusters == null) { + return Optional.empty(); + } + XdsCluster xdsCluster = clusters.get(clusterName); + if (xdsCluster == null) { + return Optional.empty(); + } + return Optional.ofNullable(xdsCluster.getInstanceCircuitBreakers()); + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsAbortTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsAbortTest.java index 5da560ade1..e97fc1e0b1 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsAbortTest.java +++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsAbortTest.java @@ -29,9 +29,9 @@ public class XdsAbortTest { @Test public void testXdsAbort() { XdsAbort abort = new XdsAbort(); - abort.setPercentage(0.1f); + abort.setPercentage(100); abort.setHttpStatus(200); Assert.assertEquals(200, abort.getHttpStatus()); - Assert.assertEquals(0.1f, abort.getPercentage(), 0); + Assert.assertEquals(100, abort.getPercentage(), 0); } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsClusterTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsClusterTest.java index cded1d59b4..e7bf88b8c9 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsClusterTest.java +++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsClusterTest.java @@ -33,9 +33,15 @@ public void testXdsCluster() { cluster.setLbPolicy(XdsLbPolicy.RANDOM); cluster.setLocalityLb(true); cluster.setServiceName("serviceA"); + XdsInstanceCircuitBreakers instanceCircuitBreakers = new XdsInstanceCircuitBreakers(); + cluster.setInstanceCircuitBreakers(instanceCircuitBreakers); + XdsRequestCircuitBreakers requestCircuitBreakers = new XdsRequestCircuitBreakers(); + cluster.setRequestCircuitBreakers(requestCircuitBreakers); Assert.assertTrue(cluster.isLocalityLb()); Assert.assertEquals("outbound|8080||serviceA.default.svc.cluster.local", cluster.getClusterName()); Assert.assertEquals("serviceA", cluster.getServiceName()); Assert.assertEquals(XdsLbPolicy.RANDOM, cluster.getLbPolicy()); + Assert.assertEquals(instanceCircuitBreakers, cluster.getInstanceCircuitBreakers()); + Assert.assertEquals(requestCircuitBreakers, cluster.getRequestCircuitBreakers()); } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsDelayTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsDelayTest.java index 2463676ef6..47fb845ee0 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsDelayTest.java +++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsDelayTest.java @@ -29,9 +29,9 @@ public class XdsDelayTest { @Test public void testXdsDelay() { XdsDelay delay = new XdsDelay(); - delay.setPercentage(0.1f); + delay.setPercentage(100); delay.setFixedDelay(200L); Assert.assertEquals(200, delay.getFixedDelay()); - Assert.assertEquals(0.1f, delay.getPercentage(), 0); + Assert.assertEquals(100, delay.getPercentage(), 0); } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakerTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakerTest.java new file mode 100644 index 0000000000..e27e7d5d8c --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsInstanceCircuitBreakerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.core.service.xds.entity; + +import org.junit.Assert; +import org.junit.Test; + +/** + * XdsInstanceCircuitBreakerTest + * + * @author zhp + * @since 2024-11-21 + **/ +public class XdsInstanceCircuitBreakerTest { + @Test + public void testXdsOutlierDetection() { + XdsInstanceCircuitBreakers xdsInstanceCircuitBreakers = initInstanceCircuitBreakers(); + Assert.assertEquals(1.0f, xdsInstanceCircuitBreakers.getFailurePercentageMinimumHosts(), 0.0f); + Assert.assertEquals(10, xdsInstanceCircuitBreakers.getConsecutiveGatewayFailure(), 0.0f); + Assert.assertEquals(10, xdsInstanceCircuitBreakers.getMaxEjectionPercent(), 0.0f); + Assert.assertEquals(20, xdsInstanceCircuitBreakers.getConsecutiveLocalOriginFailure(), 0.0f); + Assert.assertTrue(xdsInstanceCircuitBreakers.isSplitExternalLocalOriginErrors()); + Assert.assertEquals(1000L, xdsInstanceCircuitBreakers.getInterval()); + Assert.assertEquals(1000L, xdsInstanceCircuitBreakers.getBaseEjectionTime()); + Assert.assertEquals(30, xdsInstanceCircuitBreakers.getConsecutive5xxFailure()); + } + + private XdsInstanceCircuitBreakers initInstanceCircuitBreakers() { + XdsInstanceCircuitBreakers xdsInstanceCircuitBreakers = new XdsInstanceCircuitBreakers(); + xdsInstanceCircuitBreakers.setFailurePercentageMinimumHosts(1); + xdsInstanceCircuitBreakers.setConsecutiveGatewayFailure(10); + xdsInstanceCircuitBreakers.setMaxEjectionPercent(10); + xdsInstanceCircuitBreakers.setConsecutiveLocalOriginFailure(20); + xdsInstanceCircuitBreakers.setSplitExternalLocalOriginErrors(true); + xdsInstanceCircuitBreakers.setInterval(1000L); + xdsInstanceCircuitBreakers.setBaseEjectionTime(1000L); + xdsInstanceCircuitBreakers.setConsecutive5xxFailure(30); + return xdsInstanceCircuitBreakers; + } +} diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsOutlierDetectionTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsOutlierDetectionTest.java deleted file mode 100644 index c6b74ac343..0000000000 --- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsOutlierDetectionTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. - * - * 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.sermant.core.service.xds.entity; - -import org.junit.Assert; -import org.junit.Test; - -/** - * XdsOutlierDetectionTest - * - * @author zhp - * @since 2024-11-21 - **/ -public class XdsOutlierDetectionTest { - @Test - public void testXdsOutlierDetection() { - XdsOutlierDetection xdsOutlierDetection = initOutlierDetection(); - Assert.assertEquals(1.0f, xdsOutlierDetection.getFailurePercentageMinimumHosts(), 0.0f); - Assert.assertEquals(10, xdsOutlierDetection.getConsecutiveGatewayFailure(), 0.0f); - Assert.assertEquals(10, xdsOutlierDetection.getMaxEjectionPercent(), 0.0f); - Assert.assertEquals(20, xdsOutlierDetection.getConsecutiveLocalOriginFailure(), 0.0f); - Assert.assertTrue(xdsOutlierDetection.isSplitExternalLocalOriginErrors()); - Assert.assertEquals(1000L, xdsOutlierDetection.getInterval()); - Assert.assertEquals(1000L, xdsOutlierDetection.getBaseEjectionTime()); - Assert.assertEquals(30, xdsOutlierDetection.getConsecutive5xxFailure()); - } - - private XdsOutlierDetection initOutlierDetection() { - XdsOutlierDetection xdsOutlierDetection = new XdsOutlierDetection(); - xdsOutlierDetection.setFailurePercentageMinimumHosts(1.0f); - xdsOutlierDetection.setConsecutiveGatewayFailure(10); - xdsOutlierDetection.setMaxEjectionPercent(10f); - xdsOutlierDetection.setConsecutiveLocalOriginFailure(20); - xdsOutlierDetection.setSplitExternalLocalOriginErrors(true); - xdsOutlierDetection.setInterval(1000L); - xdsOutlierDetection.setBaseEjectionTime(1000L); - xdsOutlierDetection.setConsecutive5xxFailure(30); - return xdsOutlierDetection; - } -} diff --git a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsCircuitBreakersTest.java b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakersTest.java similarity index 88% rename from sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsCircuitBreakersTest.java rename to sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakersTest.java index fd9ffb72c6..b35056526a 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsCircuitBreakersTest.java +++ b/sermant-agentcore/sermant-agentcore-core/src/test/java/io/sermant/core/service/xds/entity/XdsRequestCircuitBreakersTest.java @@ -25,10 +25,10 @@ * @author zhp * @since 2024-11-21 **/ -public class XdsCircuitBreakersTest { +public class XdsRequestCircuitBreakersTest { @Test public void testXdsCircuitBreakers() { - XdsCircuitBreakers circuitBreakers = new XdsCircuitBreakers(); + XdsRequestCircuitBreakers circuitBreakers = new XdsRequestCircuitBreakers(); circuitBreakers.setMaxRequests(200); Assert.assertEquals(200, circuitBreakers.getMaxRequests()); } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java index ce2476689c..3dd6d3c6be 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/XdsCoreServiceImpl.java @@ -18,12 +18,14 @@ import io.sermant.core.common.LoggerFactory; import io.sermant.core.service.xds.XdsCoreService; +import io.sermant.core.service.xds.XdsFlowControlService; import io.sermant.core.service.xds.XdsLoadBalanceService; import io.sermant.core.service.xds.XdsRouteService; import io.sermant.core.service.xds.XdsServiceDiscovery; import io.sermant.implement.service.xds.client.XdsClient; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import io.sermant.implement.service.xds.discovery.XdsServiceDiscoveryImpl; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.flowcontrol.XdsFlowControlServiceImpl; import io.sermant.implement.service.xds.handler.CdsHandler; import io.sermant.implement.service.xds.handler.EdsHandler; import io.sermant.implement.service.xds.handler.LdsHandler; @@ -50,6 +52,8 @@ public class XdsCoreServiceImpl implements XdsCoreService { private XdsLoadBalanceService xdsLoadBalanceService; + private XdsFlowControlService xdsFlowControlService; + private XdsClient client; @Override @@ -62,15 +66,16 @@ public void start() { CdsHandler cdsHandler = new CdsHandler(client); // start to subscribe lds、rds、cds - rdsHandler.subscribe(XdsConstant.RDS_ALL_RESOURCE); - ldsHandler.subscribe(XdsConstant.LDS_ALL_RESOURCE); - cdsHandler.subscribe(XdsConstant.CDS_ALL_RESOURCE); + rdsHandler.subscribe(XdsEnvConstant.RDS_ALL_RESOURCE); + ldsHandler.subscribe(XdsEnvConstant.LDS_ALL_RESOURCE); + cdsHandler.subscribe(XdsEnvConstant.CDS_ALL_RESOURCE); // create xds service EdsHandler edsHandler = new EdsHandler(client); xdsServiceDiscovery = new XdsServiceDiscoveryImpl(edsHandler); xdsRouteService = new XdsRouteServiceImpl(); xdsLoadBalanceService = new XdsLoadBalanceServiceImpl(); + xdsFlowControlService = new XdsFlowControlServiceImpl(); } @Override @@ -96,4 +101,9 @@ public XdsRouteService getXdsRouteService() { public XdsLoadBalanceService getLoadBalanceService() { return xdsLoadBalanceService; } + + @Override + public XdsFlowControlService getXdsFlowControlService() { + return xdsFlowControlService; + } } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java index 4ff1d9588d..8a25805b1d 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/cache/XdsDataCache.java @@ -102,7 +102,7 @@ public static void updateServiceInstance(String serviceName, public static Set getServiceInstance(String serviceName) { XdsServiceClusterLoadAssigment serviceClusterInstance = SERVICE_INSTANCES.get(serviceName); if (serviceClusterInstance == null) { - return Collections.EMPTY_SET; + return Collections.emptySet(); } return serviceClusterInstance.getServiceInstance(); } @@ -150,7 +150,7 @@ public static void addServiceDiscoveryListener(String serviceName, XdsServiceDis * @return ServiceDiscoveryListeners */ public static List getServiceDiscoveryListeners(String serviceName) { - return SERVICE_DISCOVER_LISTENER.getOrDefault(serviceName, Collections.EMPTY_LIST); + return SERVICE_DISCOVER_LISTENER.getOrDefault(serviceName, Collections.emptyList()); } /** @@ -232,7 +232,7 @@ public static void updateServiceClusterMap(Map cluste public static Set getClustersByServiceName(String serviceName) { XdsServiceCluster xdsServiceCluster = serviceClusterMap.get(serviceName); if (xdsServiceCluster == null) { - return Collections.EMPTY_SET; + return Collections.emptySet(); } return xdsServiceCluster.getClusterResources(); } @@ -296,7 +296,7 @@ public static List getServiceRoute(String serviceName) { return virtualHosts.get(serviceName).getRoutes(); } } - return Collections.EMPTY_LIST; + return Collections.emptyList(); } /** diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsEnvConstant.java similarity index 90% rename from sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java rename to sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsEnvConstant.java index 7d9f3ab574..ed5b607abd 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/env/XdsConstant.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsEnvConstant.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.sermant.implement.service.xds.env; +package io.sermant.implement.service.xds.constants; /** * Constant @@ -22,7 +22,7 @@ * @author daizhenyu * @since 2024-05-09 **/ -public class XdsConstant { +public class XdsEnvConstant { /** * pod name environment */ @@ -53,16 +53,6 @@ public class XdsConstant { */ public static final String SIDECAR = "sidecar"; - /** - * ~ string - */ - public static final String WAVY_LINE = "~"; - - /** - * . string - */ - public static final String POINT = "."; - /** * hots suffix of k8s */ @@ -93,6 +83,6 @@ public class XdsConstant { */ public static final String K8S_DEFAULT_NAMESPACE = "default"; - private XdsConstant() { + private XdsEnvConstant() { } } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsFilterConstant.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsFilterConstant.java new file mode 100644 index 0000000000..a6ff1a5d67 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/constants/XdsFilterConstant.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.implement.service.xds.constants; + +/** + * Xds Filter Constant + * + * @author zhp + * @since 2024-12-11 + **/ +public class XdsFilterConstant { + /** + * filter name of http fault + */ + public static final String HTTP_FAULT_FILTER_NAME = "envoy.filters.http.fault"; + + /** + * filter name of http local rate limit filter + */ + public static final String LOCAL_RATE_LIMIT_FILTER_FILTER_NAME = "envoy.filters.http.local_ratelimit"; + + /** + * the key of token bucket with Rate limit configuration + */ + public static final String TOKEN_BUCKET = "token_bucket"; + + /** + * the key of Percentage of effective enforcement of ratelimit rule + */ + public static final String FILTER_ENFORCED = "filter_enforced"; + + /** + * the key of Percentage of enforcement of ratelimit rule + */ + public static final String FILTER_ENABLED = "filter_enabled"; + + /** + * The key of All configuration information for the response header to be added + */ + public static final String RESPONSE_HEADERS_TO_ADD = "response_headers_to_add"; + + /** + * The key of the configuration for the response header + */ + public static final String HEADER = "header"; + + /** + * The key of the append the response header + */ + public static final String APPEND = "append"; + + /** + * The key of the name of response header + */ + public static final String HEADER_KEY = "key"; + + /** + * The key of the value of response header + */ + public static final String HEADER_VALUE = "value"; + + /** + * The key of the value for FractionalPercent + */ + public static final String DEFAULT_VALUE = "default_value"; + + /** + * The key of the numerator for FractionalPercent + */ + public static final String NUMERATOR = "numerator"; + + /** + * The key of the denominator for FractionalPercent + */ + public static final String DENOMINATOR = "denominator"; + + /** + * The key of number for tokens filled each time + */ + public static final String TOKENS_PER_FILL = "tokens_per_fill"; + + /** + * The Key for the time interval of token filling + */ + public static final String FILL_INTERVAL = "fill_interval"; + + /** + * The Key for the maximum number of tokens + */ + public static final String MAX_TOKENS = "max_tokens"; + + /** + * Parse Time prefix + */ + public static final String PARSE_TIME_PREFIX = "PT"; + + private XdsFilterConstant() { + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/entity/DenominatorType.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/entity/DenominatorType.java new file mode 100644 index 0000000000..2e941b73cd --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/entity/DenominatorType.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.implement.service.xds.entity; + +import io.sermant.core.utils.StringUtils; + +/** + * Fraction percentages support several fixed denominator values + * + * @author zhp + * @since 2024-12-10 + **/ +public enum DenominatorType { + /** + * The HUNDRED means the denominator value is 100 + */ + HUNDRED(100), + /** + * The TEN_THOUSAND means the denominator value is 100000 + */ + TEN_THOUSAND(10000), + /** + * The MILLION means the denominator value is 1000000 + */ + MILLION(1000000), + /** + * UNRECOGNIZED means the denominator value indicates that it cannot fail + */ + UNRECOGNIZED(0); + + private final int value; + + DenominatorType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + /** + * Get value based on type name + * + * @param name type name + * @return value + */ + public static int getValueByName(String name) { + for (DenominatorType type : DenominatorType.values()) { + if (StringUtils.equals(name, type.name())) { + return type.value; + } + } + return UNRECOGNIZED.value; + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImpl.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImpl.java new file mode 100644 index 0000000000..1ac04a3d5e --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImpl.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.implement.service.xds.flowcontrol; + +import io.sermant.core.service.xds.XdsFlowControlService; +import io.sermant.core.service.xds.entity.XdsHttpFault; +import io.sermant.core.service.xds.entity.XdsInstanceCircuitBreakers; +import io.sermant.core.service.xds.entity.XdsRateLimit; +import io.sermant.core.service.xds.entity.XdsRequestCircuitBreakers; +import io.sermant.core.service.xds.entity.XdsRetryPolicy; +import io.sermant.core.service.xds.entity.XdsRoute; +import io.sermant.core.service.xds.entity.XdsRouteAction; +import io.sermant.core.service.xds.entity.XdsRouteConfiguration; +import io.sermant.core.service.xds.entity.XdsServiceCluster; +import io.sermant.core.service.xds.entity.XdsVirtualHost; +import io.sermant.core.utils.StringUtils; +import io.sermant.implement.service.xds.cache.XdsDataCache; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * The implementation class of XdsFlowControlService + * + * @author zhp + * @since 2024-11-27 + **/ +public class XdsFlowControlServiceImpl implements XdsFlowControlService { + /** + * constructor + */ + public XdsFlowControlServiceImpl() { + } + + @Override + public Optional getRequestCircuitBreakers(String serviceName, String clusterName) { + Map serviceClusterMap = XdsDataCache.getServiceClusterMap(); + XdsServiceCluster serviceCluster = serviceClusterMap.get(serviceName); + if (serviceCluster == null) { + return Optional.empty(); + } + return serviceCluster.getRequestCircuitBreakersOfCluster(clusterName); + } + + @Override + public Optional getInstanceCircuitBreakers(String serviceName, String clusterName) { + Map serviceClusterMap = XdsDataCache.getServiceClusterMap(); + XdsServiceCluster serviceCluster = serviceClusterMap.get(serviceName); + if (serviceCluster == null) { + return Optional.empty(); + } + return serviceCluster.getInstanceCircuitBreakersOfCluster(clusterName); + } + + @Override + public Optional getRetryPolicy(String serviceName, String routeName) { + List xdsRoutes = XdsDataCache.getServiceRoute(serviceName); + for (XdsRoute xdsRoute : xdsRoutes) { + if (StringUtils.equals(xdsRoute.getName(), routeName)) { + XdsRouteAction routeAction = xdsRoute.getRouteAction(); + return routeAction == null ? Optional.empty() : Optional.ofNullable(routeAction.getRetryPolicy()); + } + } + return Optional.empty(); + } + + @Override + public Optional getRateLimit(String serviceName, String routeName, String port) { + for (XdsRouteConfiguration routeConfiguration : XdsDataCache.getRouteConfigurations()) { + Map virtualHosts = routeConfiguration.getVirtualHosts(); + if (!virtualHosts.containsKey(serviceName)) { + continue; + } + XdsVirtualHost virtualHost = virtualHosts.get(serviceName); + if (virtualHost == null || StringUtils.isEmpty(virtualHost.getName()) + || !virtualHost.getName().contains(port)) { + continue; + } + for (XdsRoute xdsRoute : virtualHost.getRoutes()) { + if (StringUtils.equals(xdsRoute.getName(), routeName)) { + return Optional.ofNullable(xdsRoute.getRateLimit()); + } + } + } + return Optional.empty(); + } + + @Override + public Optional getHttpFault(String serviceName, String routeName) { + List xdsRoutes = XdsDataCache.getServiceRoute(serviceName); + for (XdsRoute xdsRoute : xdsRoutes) { + if (StringUtils.equals(xdsRoute.getName(), routeName)) { + return Optional.ofNullable(xdsRoute.getHttpFault()); + } + } + return Optional.empty(); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java index f2cff9f8c2..c94ce5da5b 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/CdsHandler.java @@ -29,7 +29,7 @@ import io.sermant.core.service.xds.entity.XdsServiceCluster; import io.sermant.implement.service.xds.cache.XdsDataCache; import io.sermant.implement.service.xds.client.XdsClient; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import io.sermant.implement.service.xds.utils.CdsProtocolTransformer; import java.util.Collections; @@ -52,7 +52,7 @@ public class CdsHandler extends XdsHandler { */ public CdsHandler(XdsClient client) { super(client); - this.resourceType = XdsConstant.CDS_RESOURCE_TYPE; + this.resourceType = XdsEnvConstant.CDS_RESOURCE_TYPE; } @Override @@ -76,7 +76,7 @@ private void updateEdsSubscription(Map oldServiceClus // Eds is updated based on the new service and cluster mapping relationship for (Entry> entry : XdsDataCache.getRequestObserversEntry()) { String key = entry.getKey(); - if (XdsConstant.CDS_ALL_RESOURCE.equals(key)) { + if (XdsEnvConstant.CDS_ALL_RESOURCE.equals(key)) { continue; } @@ -94,7 +94,7 @@ private void updateEdsSubscription(Map oldServiceClus continue; } StreamObserver requestStreamObserver = entry.getValue(); - requestStreamObserver.onNext(buildDiscoveryRequest(XdsConstant.EDS_RESOURCE_TYPE, null, null, + requestStreamObserver.onNext(buildDiscoveryRequest(XdsEnvConstant.EDS_RESOURCE_TYPE, null, null, XdsDataCache.getClustersByServiceName(key))); } } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java index c1b88048af..10ef3aae83 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/EdsHandler.java @@ -32,7 +32,7 @@ import io.sermant.core.utils.CollectionUtils; import io.sermant.implement.service.xds.cache.XdsDataCache; import io.sermant.implement.service.xds.client.XdsClient; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import io.sermant.implement.service.xds.utils.EdsProtocolTransformer; import java.util.List; @@ -53,7 +53,7 @@ public class EdsHandler extends XdsHandler { */ public EdsHandler(XdsClient client) { super(client); - this.resourceType = XdsConstant.EDS_RESOURCE_TYPE; + this.resourceType = XdsEnvConstant.EDS_RESOURCE_TYPE; } @Override diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/LdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/LdsHandler.java index 577ab39c57..a76af5cae5 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/LdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/LdsHandler.java @@ -28,7 +28,7 @@ import io.grpc.stub.StreamObserver; import io.sermant.implement.service.xds.cache.XdsDataCache; import io.sermant.implement.service.xds.client.XdsClient; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import io.sermant.implement.service.xds.utils.LdsProtocolTransformer; import java.util.Collections; @@ -49,7 +49,7 @@ public class LdsHandler extends XdsHandler { */ public LdsHandler(XdsClient client) { super(client); - this.resourceType = XdsConstant.LDS_RESOURCE_TYPE; + this.resourceType = XdsEnvConstant.LDS_RESOURCE_TYPE; } @Override @@ -72,8 +72,9 @@ private void updateRdsSubscription(Set oldRouteResources) { return; } StreamObserver rdsRequestObserver = XdsDataCache - .getRequestObserver(XdsConstant.RDS_ALL_RESOURCE); - rdsRequestObserver.onNext(buildDiscoveryRequest(XdsConstant.RDS_RESOURCE_TYPE, null, null, newRouteResources)); + .getRequestObserver(XdsEnvConstant.RDS_ALL_RESOURCE); + rdsRequestObserver.onNext(buildDiscoveryRequest(XdsEnvConstant.RDS_RESOURCE_TYPE, null, null, + newRouteResources)); } @Override diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/RdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/RdsHandler.java index 7bcd6645f9..081bae7ffb 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/RdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/RdsHandler.java @@ -29,7 +29,7 @@ import io.sermant.core.service.xds.entity.XdsRouteConfiguration; import io.sermant.implement.service.xds.cache.XdsDataCache; import io.sermant.implement.service.xds.client.XdsClient; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import io.sermant.implement.service.xds.utils.RdsProtocolTransformer; import java.util.List; @@ -49,7 +49,7 @@ public class RdsHandler extends XdsHandler { */ public RdsHandler(XdsClient client) { super(client); - this.resourceType = XdsConstant.RDS_RESOURCE_TYPE; + this.resourceType = XdsEnvConstant.RDS_RESOURCE_TYPE; } @Override diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java index 73b1f6ba7e..39c797baae 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/handler/XdsHandler.java @@ -29,12 +29,13 @@ import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; import io.grpc.stub.StreamObserver; +import io.sermant.core.common.CommonConstant; import io.sermant.core.common.LoggerFactory; import io.sermant.core.utils.FileUtils; import io.sermant.core.utils.NetworkUtils; import io.sermant.core.utils.StringUtils; import io.sermant.implement.service.xds.client.XdsClient; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import java.util.ArrayList; import java.util.List; @@ -146,22 +147,22 @@ protected DiscoveryRequest builtAckDiscoveryRequest(DiscoveryResponse response, } private void createNode() { - String fileContent = FileUtils.readFileToString(XdsConstant.K8S_POD_NAMESPACE_PATH); - String namespace = StringUtils.isEmpty(fileContent) ? XdsConstant.K8S_DEFAULT_NAMESPACE : fileContent; + String fileContent = FileUtils.readFileToString(XdsEnvConstant.K8S_POD_NAMESPACE_PATH); + String namespace = StringUtils.isEmpty(fileContent) ? XdsEnvConstant.K8S_DEFAULT_NAMESPACE : fileContent; StringBuilder nodeIdBuilder = new StringBuilder(); // nodeId:sidecar~{pod_ip}~{pod_name}.{namespace}~{namespace}.svc.cluster.local - nodeIdBuilder.append(XdsConstant.SIDECAR) - .append(XdsConstant.WAVY_LINE) + nodeIdBuilder.append(XdsEnvConstant.SIDECAR) + .append(CommonConstant.WAVY_LINE) .append(NetworkUtils.getMachineIp()) - .append(XdsConstant.WAVY_LINE) - .append(System.getenv(XdsConstant.POD_NAME_ENV)) - .append(XdsConstant.POINT) + .append(CommonConstant.WAVY_LINE) + .append(System.getenv(XdsEnvConstant.POD_NAME_ENV)) + .append(CommonConstant.DOT) .append(namespace) - .append(XdsConstant.WAVY_LINE) + .append(CommonConstant.WAVY_LINE) .append(namespace) - .append(XdsConstant.POINT) - .append(XdsConstant.HOST_SUFFIX); + .append(CommonConstant.DOT) + .append(XdsEnvConstant.HOST_SUFFIX); this.node = Node.newBuilder() .setId(nodeIdBuilder.toString()) .build(); diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/CdsProtocolTransformer.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/CdsProtocolTransformer.java index 490fb04ccb..061e197131 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/CdsProtocolTransformer.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/CdsProtocolTransformer.java @@ -16,11 +16,16 @@ package io.sermant.implement.service.xds.utils; +import io.envoyproxy.envoy.config.cluster.v3.CircuitBreakers; import io.envoyproxy.envoy.config.cluster.v3.Cluster; import io.envoyproxy.envoy.config.cluster.v3.Cluster.LbPolicy; +import io.envoyproxy.envoy.config.cluster.v3.OutlierDetection; import io.sermant.core.service.xds.entity.XdsCluster; +import io.sermant.core.service.xds.entity.XdsInstanceCircuitBreakers; import io.sermant.core.service.xds.entity.XdsLbPolicy; +import io.sermant.core.service.xds.entity.XdsRequestCircuitBreakers; import io.sermant.core.service.xds.entity.XdsServiceCluster; +import io.sermant.core.utils.CollectionUtils; import io.sermant.core.utils.StringUtils; import java.util.HashMap; @@ -65,7 +70,7 @@ private CdsProtocolTransformer() { public static Map getServiceClusters(List clusters) { Map> xdsClusters = clusters.stream() .filter(Objects::nonNull) - .map(cluster -> parseCluster(cluster)) + .map(CdsProtocolTransformer::parseCluster) .filter(xdsCluster -> !StringUtils.isEmpty(xdsCluster.getServiceName())) .collect(Collectors.groupingBy( XdsCluster::getServiceName, @@ -96,6 +101,8 @@ private static XdsCluster parseCluster(Cluster cluster) { xdsCluster.setServiceName(serviceNameFromCluster.get()); xdsCluster.setLocalityLb(cluster.getCommonLbConfig().hasLocalityWeightedLbConfig()); xdsCluster.setLbPolicy(parseClusterLbPolicy(cluster.getLbPolicy())); + xdsCluster.setRequestCircuitBreakers(parseRequestCircuitBreakers(cluster.getCircuitBreakers())); + xdsCluster.setInstanceCircuitBreakers(parseInstanceCircuitBreakers(cluster)); return xdsCluster; } @@ -113,4 +120,35 @@ private static String getServiceBaseClusterName(Set xdsClusters) { private static XdsLbPolicy parseClusterLbPolicy(LbPolicy lbPolicy) { return LB_POLICY_MAPPING.getOrDefault(lbPolicy, XdsLbPolicy.UNRECOGNIZED); } + + private static XdsRequestCircuitBreakers parseRequestCircuitBreakers(CircuitBreakers circuitBreakers) { + XdsRequestCircuitBreakers requestCircuitBreakers = new XdsRequestCircuitBreakers(); + if (!CollectionUtils.isEmpty(circuitBreakers.getThresholdsList())) { + requestCircuitBreakers.setMaxRequests(circuitBreakers.getThresholds(0).getMaxRequests().getValue()); + } + return requestCircuitBreakers; + } + + private static XdsInstanceCircuitBreakers parseInstanceCircuitBreakers(Cluster cluster) { + OutlierDetection outlierDetection = cluster.getOutlierDetection(); + XdsInstanceCircuitBreakers xdsInstanceCircuitBreakers = new XdsInstanceCircuitBreakers(); + xdsInstanceCircuitBreakers.setSplitExternalLocalOriginErrors(outlierDetection + .getSplitExternalLocalOriginErrors()); + xdsInstanceCircuitBreakers.setConsecutiveLocalOriginFailure(outlierDetection.getConsecutiveLocalOriginFailure() + .getValue()); + xdsInstanceCircuitBreakers.setConsecutiveGatewayFailure(outlierDetection.getConsecutiveGatewayFailure() + .getValue()); + xdsInstanceCircuitBreakers.setConsecutive5xxFailure(outlierDetection.getConsecutive5Xx().getValue()); + long interval = java.time.Duration.ofSeconds(outlierDetection.getInterval().getSeconds()).toMillis(); + xdsInstanceCircuitBreakers.setInterval(interval); + long ejectionTime = java.time.Duration.ofSeconds(outlierDetection.getBaseEjectionTime().getSeconds()) + .toMillis(); + xdsInstanceCircuitBreakers.setBaseEjectionTime(ejectionTime); + xdsInstanceCircuitBreakers.setMaxEjectionPercent(outlierDetection.getMaxEjectionPercent().getValue()); + xdsInstanceCircuitBreakers.setFailurePercentageMinimumHosts(outlierDetection.getFailurePercentageMinimumHosts() + .getValue()); + xdsInstanceCircuitBreakers.setMinHealthPercent(cluster.getCommonLbConfig(). + getHealthyPanicThreshold().getValue()); + return xdsInstanceCircuitBreakers; + } } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java index c8448b2b8d..d8963ccc47 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/xds/utils/RdsProtocolTransformer.java @@ -16,7 +16,16 @@ package io.sermant.implement.service.xds.utils; +import com.github.udpa.udpa.type.v1.TypedStruct; +import com.google.protobuf.Any; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ListValue; +import com.google.protobuf.Message; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; + import io.envoyproxy.envoy.config.route.v3.HeaderMatcher; +import io.envoyproxy.envoy.config.route.v3.RetryPolicy; import io.envoyproxy.envoy.config.route.v3.Route; import io.envoyproxy.envoy.config.route.v3.RouteAction; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; @@ -24,16 +33,28 @@ import io.envoyproxy.envoy.config.route.v3.VirtualHost; import io.envoyproxy.envoy.config.route.v3.WeightedCluster; import io.envoyproxy.envoy.config.route.v3.WeightedCluster.ClusterWeight; +import io.envoyproxy.envoy.extensions.filters.common.fault.v3.FaultDelay; +import io.envoyproxy.envoy.extensions.filters.http.fault.v3.FaultAbort; +import io.envoyproxy.envoy.extensions.filters.http.fault.v3.HTTPFault; import io.envoyproxy.envoy.type.matcher.v3.StringMatcher; +import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.sermant.core.common.LoggerFactory; +import io.sermant.core.service.xds.entity.XdsAbort; +import io.sermant.core.service.xds.entity.XdsDelay; +import io.sermant.core.service.xds.entity.XdsHeader; import io.sermant.core.service.xds.entity.XdsHeaderMatcher; +import io.sermant.core.service.xds.entity.XdsHeaderOption; +import io.sermant.core.service.xds.entity.XdsHttpFault; import io.sermant.core.service.xds.entity.XdsPathMatcher; +import io.sermant.core.service.xds.entity.XdsRateLimit; +import io.sermant.core.service.xds.entity.XdsRetryPolicy; import io.sermant.core.service.xds.entity.XdsRoute; import io.sermant.core.service.xds.entity.XdsRouteAction; import io.sermant.core.service.xds.entity.XdsRouteAction.XdsClusterWeight; import io.sermant.core.service.xds.entity.XdsRouteAction.XdsWeightedClusters; import io.sermant.core.service.xds.entity.XdsRouteConfiguration; import io.sermant.core.service.xds.entity.XdsRouteMatch; +import io.sermant.core.service.xds.entity.XdsTokenBucket; import io.sermant.core.service.xds.entity.XdsVirtualHost; import io.sermant.core.service.xds.entity.match.ExactMatchStrategy; import io.sermant.core.service.xds.entity.match.PrefixMatchStrategy; @@ -41,11 +62,19 @@ import io.sermant.core.service.xds.entity.match.RegexMatchStrategy; import io.sermant.core.service.xds.entity.match.SuffixMatchStrategy; import io.sermant.core.service.xds.entity.match.UnknownMatchStrategy; +import io.sermant.core.utils.StringUtils; +import io.sermant.implement.service.xds.constants.XdsFilterConstant; +import io.sermant.implement.service.xds.entity.DenominatorType; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -73,7 +102,7 @@ private RdsProtocolTransformer() { public static List getRouteConfigurations(List routeConfigurations) { return routeConfigurations.stream() .filter(Objects::nonNull) - .map(routeConfiguration -> parseRouteConfiguration(routeConfiguration)) + .map(RdsProtocolTransformer::parseRouteConfiguration) .collect(Collectors.toList()); } @@ -81,7 +110,7 @@ private static XdsRouteConfiguration parseRouteConfiguration(RouteConfiguration XdsRouteConfiguration xdsRouteConfiguration = new XdsRouteConfiguration(); xdsRouteConfiguration.setRouteConfigName(routeConfiguration.getName()); Map xdsVirtualHostMap = routeConfiguration.getVirtualHostsList().stream() - .map(virtualHost -> parseVirtualHost(virtualHost)) + .map(RdsProtocolTransformer::parseVirtualHost) .collect(Collectors.toMap( xdsVirtualHost -> xdsVirtualHost.getName().split(POINT_SEPARATOR)[0], xdsVirtualHost -> xdsVirtualHost @@ -95,7 +124,7 @@ private static XdsVirtualHost parseVirtualHost(VirtualHost virtualHost) { List domains = virtualHost.getDomainsList(); List xdsRoutes = virtualHost.getRoutesList().stream() - .map(route -> parseRoute(route)) + .map(RdsProtocolTransformer::parseRoute) .collect(Collectors.toList()); xdsVirtualHost.setName(virtualHost.getName()); xdsVirtualHost.setRoutes(xdsRoutes); @@ -108,6 +137,22 @@ private static XdsRoute parseRoute(Route route) { xdsRoute.setName(route.getName()); xdsRoute.setRouteMatch(parseRouteMatch(route.getMatch())); xdsRoute.setRouteAction(parseRouteAction(route.getRoute())); + for (Map.Entry entry : route.getTypedPerFilterConfigMap().entrySet()) { + if (entry.getValue() == null) { + continue; + } + if (StringUtils.equals(entry.getKey(), XdsFilterConstant.HTTP_FAULT_FILTER_NAME)) { + Optional optional = unpackAndParseFilter(entry.getValue(), HTTPFault.class, + RdsProtocolTransformer::parseHttpFault); + optional.ifPresent(xdsRoute::setHttpFault); + continue; + } + if (StringUtils.equals(entry.getKey(), XdsFilterConstant.LOCAL_RATE_LIMIT_FILTER_FILTER_NAME)) { + Optional optional = unpackAndParseFilter(entry.getValue(), TypedStruct.class, + RdsProtocolTransformer::parseRateLimit); + optional.ifPresent(xdsRoute::setRateLimit); + } + } return xdsRoute; } @@ -122,7 +167,7 @@ private static XdsRouteMatch parseRouteMatch(RouteMatch routeMatch) { private static List parseHeaderMatchers(List headerMatchers) { return headerMatchers.stream() .filter(Objects::nonNull) - .map(headerMatcher -> parseHeaderMatcher(headerMatcher)) + .map(RdsProtocolTransformer::parseHeaderMatcher) .collect(Collectors.toList()); } @@ -192,6 +237,7 @@ private static XdsRouteAction parseRouteAction(RouteAction routeAction) { LOGGER.log(Level.WARNING, "The xDS route action strategy is unknown. Please check the route configuration."); } + xdsRouteAction.setRetryPolicy(parseRetryPolicy(routeAction.getRetryPolicy())); return xdsRouteAction; } @@ -204,7 +250,7 @@ private static XdsWeightedClusters parseWeightedClusters(WeightedCluster cluster ); xdsWeightedClusters.setClusters(clusters.getClustersList().stream() .filter(Objects::nonNull) - .map(clusterWeight -> parseClusterWeight(clusterWeight)) + .map(RdsProtocolTransformer::parseClusterWeight) .collect(Collectors.toList())); return xdsWeightedClusters; } @@ -215,4 +261,180 @@ private static XdsClusterWeight parseClusterWeight(ClusterWeight clusterWeight) xdsClusterWeight.setClusterName(clusterWeight.getName()); return xdsClusterWeight; } + + private static XdsRetryPolicy parseRetryPolicy(RetryPolicy retryPolicy) { + XdsRetryPolicy xdsRetryPolicy = new XdsRetryPolicy(); + xdsRetryPolicy.setRetryOn(retryPolicy.getRetryOn()); + xdsRetryPolicy.setMaxAttempts(retryPolicy.getHostSelectionRetryMaxAttempts()); + long perTryTimeout = Duration.ofSeconds(retryPolicy.getPerTryTimeout().getSeconds()).toMillis(); + xdsRetryPolicy.setPerTryTimeout(perTryTimeout); + if (retryPolicy.getRetryHostPredicateCount() != 0) { + xdsRetryPolicy.setRetryHostPredicate(retryPolicy.getRetryHostPredicate(0).getName()); + } + return xdsRetryPolicy; + } + + private static Optional unpackAndParseFilter(Any value, Class clazz, + Function parser) { + try { + T filter = value.unpack(clazz); + return Optional.of(parser.apply(filter)); + } catch (InvalidProtocolBufferException e) { + LOGGER.log(Level.SEVERE, "Failed to unpack and parse filter of type: " + clazz.getName(), e); + } + return Optional.empty(); + } + + private static XdsHttpFault parseHttpFault(HTTPFault httpFault) { + XdsHttpFault xdsHttpFault = new XdsHttpFault(); + xdsHttpFault.setAbort(parseAbort(httpFault.getAbort())); + xdsHttpFault.setDelay(parseDelay(httpFault.getDelay())); + return xdsHttpFault; + } + + private static XdsAbort parseAbort(FaultAbort faultAbort) { + XdsAbort xdsAbort = new XdsAbort(); + xdsAbort.setPercentage(faultAbort.getPercentage().getNumerator()); + xdsAbort.setHttpStatus(faultAbort.getHttpStatus()); + return xdsAbort; + } + + private static XdsDelay parseDelay(FaultDelay faultDelay) { + XdsDelay xdsDelay = new XdsDelay(); + long fixedDelay = Duration.ofSeconds(faultDelay.getFixedDelay().getSeconds()).toMillis(); + xdsDelay.setFixedDelay(fixedDelay); + xdsDelay.setPercentage(faultDelay.getPercentage().getNumerator()); + return xdsDelay; + } + + /** + * Parse Rate limiting configuration information + * The original message format of LocalRateLimit can be found in the link: + * LocalRateLimit + * The format of some of the obtained LocalRateLimit messages is as follows: + * fields { + * key: "filter_enabled" + * value { + * struct_value { + * fields { + * key: "default_value" + * value { + * struct_value { + * fields { + * key: "denominator" + * value { + * string_value: "HUNDRED" + * } + * } + * fields { + * key: "numerator" + * value { + * number_value: 100.0 + * } + * } + * } + * } + * } + * } + * } + *} + * + * @param typedStruct Serial protocol buffer message for rate limiting configuration + * @return Rate limiting configuration information + */ + private static XdsRateLimit parseRateLimit(TypedStruct typedStruct) { + XdsRateLimit xdsRateLimit = new XdsRateLimit(); + Struct struct = typedStruct.getValue(); + if (struct.containsFields(XdsFilterConstant.TOKEN_BUCKET)) { + Optional optionalTokenBucket = + parseTokenBucket(struct.getFieldsOrThrow(XdsFilterConstant.TOKEN_BUCKET).getStructValue()); + optionalTokenBucket.ifPresent(xdsRateLimit::setTokenBucket); + } + if (struct.containsFields(XdsFilterConstant.FILTER_ENFORCED)) { + Optional optionalXdsFractionalPercent = + parseRuntimeFractionalPercent(struct.getFieldsOrThrow(XdsFilterConstant.FILTER_ENFORCED) + .getStructValue()); + optionalXdsFractionalPercent.ifPresent(xdsRateLimit::setPercent); + } + if (struct.containsFields(XdsFilterConstant.FILTER_ENABLED) + && (xdsRateLimit.getPercent() == null || xdsRateLimit.getPercent().getNumerator() == 0)) { + Optional optionalXdsFractionalPercent = + parseRuntimeFractionalPercent(struct.getFieldsOrThrow(XdsFilterConstant.FILTER_ENABLED) + .getStructValue()); + optionalXdsFractionalPercent.ifPresent(xdsRateLimit::setPercent); + } + List responseHeaders = parseHeaderValueOptions(struct); + xdsRateLimit.setResponseHeaderOption(responseHeaders); + return xdsRateLimit; + } + + private static List parseHeaderValueOptions(Struct struct) { + if (!struct.containsFields(XdsFilterConstant.RESPONSE_HEADERS_TO_ADD)) { + return Collections.emptyList(); + } + List responseHeaders = new ArrayList<>(); + ListValue headers = struct.getFieldsOrThrow(XdsFilterConstant.RESPONSE_HEADERS_TO_ADD).getListValue(); + for (Value value : headers.getValuesList()) { + Map headersMap = value.getStructValue().getFieldsMap(); + if (headersMap.get(XdsFilterConstant.HEADER) == null) { + continue; + } + Struct headerStruct = headersMap.get(XdsFilterConstant.HEADER).getStructValue(); + XdsHeaderOption responseHeader = new XdsHeaderOption(); + if (headersMap.get(XdsFilterConstant.APPEND) != null) { + responseHeader.setEnabledAppend(headersMap.get(XdsFilterConstant.APPEND).getBoolValue()); + } + if (!headerStruct.containsFields(XdsFilterConstant.HEADER_KEY)) { + continue; + } + XdsHeader xdsHeader = new XdsHeader(); + xdsHeader.setKey(headerStruct.getFieldsOrThrow(XdsFilterConstant.HEADER_KEY).getStringValue()); + if (headerStruct.containsFields(XdsFilterConstant.HEADER_VALUE)) { + xdsHeader.setValue(headerStruct.getFieldsOrThrow(XdsFilterConstant.HEADER_VALUE).getStringValue()); + } + responseHeader.setHeader(xdsHeader); + responseHeaders.add(responseHeader); + } + return responseHeaders; + } + + private static Optional parseRuntimeFractionalPercent( + Struct filterEnabledStruct) { + Map valueMap = filterEnabledStruct.getFieldsMap(); + Value defaultValue = valueMap.get(XdsFilterConstant.DEFAULT_VALUE); + if (defaultValue == null) { + return Optional.empty(); + } + Struct defaultValueStruct = defaultValue.getStructValue(); + Map defaultValueMap = defaultValueStruct.getFieldsMap(); + if (defaultValueMap.get(XdsFilterConstant.DENOMINATOR) == null + || defaultValueMap.get(XdsFilterConstant.NUMERATOR) == null) { + return Optional.empty(); + } + io.sermant.core.service.xds.entity.FractionalPercent fractionalPercent = + new io.sermant.core.service.xds.entity.FractionalPercent(); + fractionalPercent.setNumerator((int) defaultValueMap.get(XdsFilterConstant.NUMERATOR).getNumberValue()); + FractionalPercent.DenominatorType type = FractionalPercent.DenominatorType.valueOf( + defaultValueMap.get(XdsFilterConstant.DENOMINATOR).getStringValue()); + fractionalPercent.setDenominator(DenominatorType.getValueByName(type.name())); + return Optional.of(fractionalPercent); + } + + private static Optional parseTokenBucket(Struct tokenBucketStruct) { + Map fieldMap = tokenBucketStruct.getFieldsMap(); + if (fieldMap.get(XdsFilterConstant.TOKENS_PER_FILL) == null + || fieldMap.get(XdsFilterConstant.FILL_INTERVAL) == null + || fieldMap.get(XdsFilterConstant.MAX_TOKENS) == null) { + return Optional.empty(); + } + double tokensPerFill = fieldMap.get(XdsFilterConstant.TOKENS_PER_FILL).getNumberValue(); + String timeStr = XdsFilterConstant.PARSE_TIME_PREFIX + fieldMap.get(XdsFilterConstant.FILL_INTERVAL) + .getStringValue(); + long fillInterval = Duration.parse(timeStr).toMillis(); + XdsTokenBucket xdsTokenBucket = new XdsTokenBucket(); + xdsTokenBucket.setFillInterval(fillInterval); + xdsTokenBucket.setMaxTokens((int) fieldMap.get(XdsFilterConstant.MAX_TOKENS).getNumberValue()); + xdsTokenBucket.setTokensPerFill((int) tokensPerFill); + return Optional.of(xdsTokenBucket); + } } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/CommonDataGenerator.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/CommonDataGenerator.java index 3519804254..8c2083fcac 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/CommonDataGenerator.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/CommonDataGenerator.java @@ -17,20 +17,25 @@ package io.sermant.implement.service.xds; import io.sermant.core.service.xds.entity.ServiceInstance; +import io.sermant.core.service.xds.entity.XdsAbort; import io.sermant.core.service.xds.entity.XdsCluster; import io.sermant.core.service.xds.entity.XdsClusterLoadAssigment; import io.sermant.core.service.xds.entity.XdsHttpConnectionManager; +import io.sermant.core.service.xds.entity.XdsHttpFault; +import io.sermant.core.service.xds.entity.XdsInstanceCircuitBreakers; import io.sermant.core.service.xds.entity.XdsLbPolicy; import io.sermant.core.service.xds.entity.XdsLocality; +import io.sermant.core.service.xds.entity.XdsRateLimit; +import io.sermant.core.service.xds.entity.XdsRequestCircuitBreakers; import io.sermant.core.service.xds.entity.XdsRoute; import io.sermant.core.service.xds.entity.XdsRouteConfiguration; import io.sermant.core.service.xds.entity.XdsServiceCluster; import io.sermant.core.service.xds.entity.XdsServiceClusterLoadAssigment; +import io.sermant.core.service.xds.entity.XdsTokenBucket; import io.sermant.core.service.xds.entity.XdsVirtualHost; import io.sermant.implement.service.xds.entity.XdsServiceInstance; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -46,26 +51,32 @@ public class CommonDataGenerator { public static List createRouteConfigurations() { XdsRouteConfiguration routeConfiguration = new XdsRouteConfiguration(); XdsVirtualHost virtualHost1 = new XdsVirtualHost(); - virtualHost1.setName("serviceA.name"); + virtualHost1.setName("serviceA.name:8080"); XdsVirtualHost virtualHost2 = new XdsVirtualHost(); - virtualHost2.setName("serviceB.name"); + virtualHost2.setName("serviceB.name:8080"); XdsRoute route = new XdsRoute(); route.setName("test-route"); - virtualHost1.setRoutes(Arrays.asList(route)); + XdsHttpFault httpFault = new XdsHttpFault(); + httpFault.setAbort(new XdsAbort()); + httpFault.getAbort().setHttpStatus(503); + route.setHttpFault(httpFault); + XdsRateLimit xdsRateLimit = new XdsRateLimit(); + XdsTokenBucket xdsTokenBucket = new XdsTokenBucket(); + xdsTokenBucket.setMaxTokens(10); + xdsRateLimit.setTokenBucket(xdsTokenBucket); + route.setRateLimit(xdsRateLimit); + virtualHost1.setRoutes(Collections.singletonList(route)); virtualHost2.setRoutes(Collections.emptyList()); Map virtualHosts = new HashMap<>(); virtualHosts.put("serviceA", virtualHost1); virtualHosts.put("serviceB", virtualHost2); routeConfiguration.setVirtualHosts(virtualHosts); - return Arrays.asList(routeConfiguration); + return Collections.singletonList(routeConfiguration); } public static Map createServiceClusterMap(String serviceName, String clusterName) { - XdsCluster cluster = new XdsCluster(); - cluster.setClusterName(clusterName); - cluster.setLocalityLb(true); - cluster.setLbPolicy(XdsLbPolicy.RANDOM); + XdsCluster cluster = initCluster(clusterName); Map clusters = new HashMap<>(); clusters.put(clusterName, cluster); @@ -78,6 +89,20 @@ public static Map createServiceClusterMap(String serv return serviceClusterMap; } + private static XdsCluster initCluster(String clusterName) { + XdsCluster cluster = new XdsCluster(); + cluster.setClusterName(clusterName); + cluster.setLocalityLb(true); + cluster.setLbPolicy(XdsLbPolicy.RANDOM); + XdsRequestCircuitBreakers requestCircuitBreakers = new XdsRequestCircuitBreakers(); + requestCircuitBreakers.setMaxRequests(100); + cluster.setRequestCircuitBreakers(requestCircuitBreakers); + XdsInstanceCircuitBreakers instanceCircuitBreakers = new XdsInstanceCircuitBreakers(); + instanceCircuitBreakers.setInterval(1000); + cluster.setInstanceCircuitBreakers(instanceCircuitBreakers); + return cluster; + } + public static XdsServiceClusterLoadAssigment createXdsServiceClusterInstance(List clusterNames, String baseClusterName) { Map clusterInstances = new HashMap<>(); diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImplTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImplTest.java new file mode 100644 index 0000000000..1c4550b96b --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/flowcontrol/XdsFlowControlServiceImplTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. All rights reserved. + * + * 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.sermant.implement.service.xds.flowcontrol; + +import io.sermant.core.service.xds.XdsFlowControlService; +import io.sermant.core.service.xds.entity.XdsHttpFault; +import io.sermant.core.service.xds.entity.XdsInstanceCircuitBreakers; +import io.sermant.core.service.xds.entity.XdsRateLimit; +import io.sermant.core.service.xds.entity.XdsRequestCircuitBreakers; +import io.sermant.implement.service.xds.BaseXdsTest; +import io.sermant.implement.service.xds.CommonDataGenerator; +import io.sermant.implement.service.xds.cache.XdsDataCache; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Optional; + +/** + * XdsFlowControlServiceImplTest + * + * @author zhp + * @since 2024-12-02 + **/ +public class XdsFlowControlServiceImplTest extends BaseXdsTest { + private static final String SERVICE_NAME = "serviceA"; + + private static final String ROUTE_NAME = "test-route"; + + private static final String CLUSTER_NAME = "outbound|8080||serviceA.default.svc.cluster.local"; + + private static final String PORT = "8080"; + + private static XdsFlowControlService flowControlService; + + @BeforeClass + public static void setUp() { + flowControlService = new XdsFlowControlServiceImpl(); + XdsDataCache.updateRouteConfigurations(CommonDataGenerator.createRouteConfigurations()); + XdsDataCache.updateServiceClusterMap(CommonDataGenerator + .createServiceClusterMap(SERVICE_NAME, CLUSTER_NAME)); + } + + @AfterClass + public static void tearDown() { + XdsDataCache.updateRouteConfigurations(new ArrayList<>()); + XdsDataCache.updateServiceClusterMap(new HashMap<>()); + } + + @Test + public void testGetRequestCircuitBreakers() { + Optional result = flowControlService.getRequestCircuitBreakers(SERVICE_NAME, CLUSTER_NAME); + Assert.assertTrue(result.isPresent()); + XdsRequestCircuitBreakers requestCircuitBreakers = result.get(); + Assert.assertEquals(100, requestCircuitBreakers.getMaxRequests()); + } + + @Test + public void testGetInstanceCircuitBreakers() { + Optional result = flowControlService.getInstanceCircuitBreakers(SERVICE_NAME, CLUSTER_NAME); + Assert.assertTrue(result.isPresent()); + XdsInstanceCircuitBreakers instanceCircuitBreakers = result.get(); + Assert.assertEquals(1000, instanceCircuitBreakers.getInterval()); + } + + @Test + public void testGetHttpFault() { + Optional result = flowControlService.getHttpFault(SERVICE_NAME, ROUTE_NAME); + Assert.assertTrue(result.isPresent()); + XdsHttpFault httpFault = result.get(); + Assert.assertEquals(503, httpFault.getAbort().getHttpStatus()); + } + + @Test + public void testGetRateLimit() { + Optional result = flowControlService.getRateLimit(SERVICE_NAME, ROUTE_NAME, PORT); + Assert.assertTrue(result.isPresent()); + XdsRateLimit rateLimit = result.get(); + Assert.assertEquals(10, rateLimit.getTokenBucket().getMaxTokens()); + } +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsHandlerTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsHandlerTest.java index 346fce04a2..a846a908d8 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsHandlerTest.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/CdsHandlerTest.java @@ -22,7 +22,7 @@ import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; import io.sermant.implement.service.xds.BaseXdsTest; import io.sermant.implement.service.xds.cache.XdsDataCache; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import org.junit.AfterClass; import org.junit.Assert; @@ -49,7 +49,7 @@ public class CdsHandlerTest extends BaseXdsTest { public static void setUp() { handler = new CdsHandler(client); Mockito.doReturn(requestStreamObserver).when(client).getDiscoveryRequestObserver(handler - .getResponseStreamObserver(XdsConstant.CDS_ALL_RESOURCE, null)); + .getResponseStreamObserver(XdsEnvConstant.CDS_ALL_RESOURCE, null)); XdsDataCache.updateRequestObserver(serviceName, requestStreamObserver); } @@ -57,21 +57,21 @@ public static void setUp() { public static void tearDown() { Mockito.clearAllCaches(); XdsDataCache.removeRequestObserver(serviceName); - XdsDataCache.removeRequestObserver(XdsConstant.CDS_ALL_RESOURCE); + XdsDataCache.removeRequestObserver(XdsEnvConstant.CDS_ALL_RESOURCE); XdsDataCache.updateServiceClusterMap(new HashMap<>()); } @Test public void testHandleResponse() { - handler.subscribe(XdsConstant.CDS_ALL_RESOURCE, null); + handler.subscribe(XdsEnvConstant.CDS_ALL_RESOURCE, null); // cluster is empty - handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.CDS_ALL_RESOURCE, buildDiscoveryResponse(new ArrayList<>())); Assert.assertEquals(0, XdsDataCache.getServiceClusterMap().size()); // service with one cluster - handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.CDS_ALL_RESOURCE, buildDiscoveryResponse(Arrays.asList("outbound|8080||serviceA.default.svc.cluster.local"))); Set clusterNames = XdsDataCache.getClustersByServiceName(serviceName); Assert.assertNotNull(clusterNames); @@ -79,7 +79,7 @@ public void testHandleResponse() { Assert.assertTrue(clusterNames.contains("outbound|8080||serviceA.default.svc.cluster.local")); // service with many cluster - handler.handleResponse(XdsConstant.CDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.CDS_ALL_RESOURCE, buildDiscoveryResponse(Arrays.asList( "outbound|8080|subset1|serviceA.default.svc.cluster.local", "outbound|8080|subset2|serviceA.default.svc.cluster.local" diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/LdsHandlerTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/LdsHandlerTest.java index e6baf98dca..572a332755 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/LdsHandlerTest.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/LdsHandlerTest.java @@ -26,7 +26,7 @@ import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; import io.sermant.implement.service.xds.BaseXdsTest; import io.sermant.implement.service.xds.cache.XdsDataCache; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import org.junit.AfterClass; import org.junit.Assert; @@ -51,29 +51,29 @@ public class LdsHandlerTest extends BaseXdsTest { public static void setUp() { handler = new LdsHandler(client); Mockito.doReturn(requestStreamObserver).when(client).getDiscoveryRequestObserver(handler - .getResponseStreamObserver(XdsConstant.LDS_ALL_RESOURCE, null)); - XdsDataCache.updateRequestObserver(XdsConstant.RDS_ALL_RESOURCE, requestStreamObserver); + .getResponseStreamObserver(XdsEnvConstant.LDS_ALL_RESOURCE, null)); + XdsDataCache.updateRequestObserver(XdsEnvConstant.RDS_ALL_RESOURCE, requestStreamObserver); } @AfterClass public static void tearDown() { Mockito.clearAllCaches(); - XdsDataCache.removeRequestObserver(XdsConstant.LDS_ALL_RESOURCE); - XdsDataCache.removeRequestObserver(XdsConstant.RDS_ALL_RESOURCE); + XdsDataCache.removeRequestObserver(XdsEnvConstant.LDS_ALL_RESOURCE); + XdsDataCache.removeRequestObserver(XdsEnvConstant.RDS_ALL_RESOURCE); XdsDataCache.updateHttpConnectionManagers(new ArrayList<>()); } @Test public void testHandleResponse() { - handler.subscribe(XdsConstant.LDS_ALL_RESOURCE, null); + handler.subscribe(XdsEnvConstant.LDS_ALL_RESOURCE, null); // listener is empty - handler.handleResponse(XdsConstant.LDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.LDS_ALL_RESOURCE, DiscoveryResponse.newBuilder().addAllResources(new ArrayList<>()).build()); Assert.assertEquals(0, XdsDataCache.getRouteResources().size()); // listener is not empty - handler.handleResponse(XdsConstant.LDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.LDS_ALL_RESOURCE, buildDiscoveryResponse("test-listener", "test-routeConfig")); Set routeResources = XdsDataCache.getRouteResources(); Assert.assertEquals(1, routeResources.size()); diff --git a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/RdsHandlerTest.java b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/RdsHandlerTest.java index 2ee54fa3f9..ee6d4d41e9 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/RdsHandlerTest.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/test/java/io/sermant/implement/service/xds/handler/RdsHandlerTest.java @@ -25,7 +25,7 @@ import io.sermant.core.service.xds.entity.XdsRoute; import io.sermant.implement.service.xds.BaseXdsTest; import io.sermant.implement.service.xds.cache.XdsDataCache; -import io.sermant.implement.service.xds.env.XdsConstant; +import io.sermant.implement.service.xds.constants.XdsEnvConstant; import org.junit.After; import org.junit.Assert; @@ -49,27 +49,27 @@ public class RdsHandlerTest extends BaseXdsTest { public void setUp() throws Exception { handler = new RdsHandler(client); Mockito.doReturn(requestStreamObserver).when(client).getDiscoveryRequestObserver(handler - .getResponseStreamObserver(XdsConstant.RDS_ALL_RESOURCE, null)); + .getResponseStreamObserver(XdsEnvConstant.RDS_ALL_RESOURCE, null)); } @After public void tearDown() throws Exception { Mockito.clearAllCaches(); - XdsDataCache.removeRequestObserver(XdsConstant.RDS_ALL_RESOURCE); + XdsDataCache.removeRequestObserver(XdsEnvConstant.RDS_ALL_RESOURCE); XdsDataCache.updateRouteConfigurations(new ArrayList<>()); } @Test public void testHandleResponse() { - handler.subscribe(XdsConstant.RDS_ALL_RESOURCE, null); + handler.subscribe(XdsEnvConstant.RDS_ALL_RESOURCE, null); // routeConfiguration is empty - handler.handleResponse(XdsConstant.RDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.RDS_ALL_RESOURCE, DiscoveryResponse.newBuilder().addAllResources(new ArrayList<>()).build()); Assert.assertEquals(0, XdsDataCache.getRouteConfigurations().size()); // routeConfiguration is not empty - handler.handleResponse(XdsConstant.RDS_ALL_RESOURCE, + handler.handleResponse(XdsEnvConstant.RDS_ALL_RESOURCE, buildDiscoveryResponse("serviceA.example.com", "test-route", "test-routeConfig")); List route = XdsDataCache.getServiceRoute("serviceA");