This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git
commit 28eedb4b2493430d39e82903ba87010edd0e06ef Author: maheshrajus <[email protected]> AuthorDate: Tue Mar 20 22:27:24 2018 +0530 Fault-Injection handler implementation --- coverage-reports/pom.xml | 4 + handlers/handler-fault-injection/pom.xml | 44 ++ .../servicecomb/faultinjection/AbortFault.java | 64 ++ .../servicecomb/faultinjection/AbstractFault.java | 25 + .../servicecomb/faultinjection/BeanHolder.java | 47 ++ .../servicecomb/faultinjection/DelayFault.java | 81 +++ .../apache/servicecomb/faultinjection/Fault.java | 27 + .../faultinjection/FaultInjectionConfig.java | 59 ++ .../faultinjection/FaultInjectionConst.java | 36 ++ .../faultinjection/FaultInjectionHandler.java | 72 +++ .../faultinjection/FaultInjectionUtil.java | 168 +++++ .../servicecomb/faultinjection/FaultParam.java | 37 ++ .../servicecomb/faultinjection/FaultResponse.java | 63 ++ .../main/resources/META-INF/spring/cse.bean.xml} | 37 +- .../src/main/resources/config/cse.handler.xml | 21 + .../faultinjection/TestFaultInjectConfig.java | 102 +++ .../faultinjection/TestFaultInjectHandler.java | 683 +++++++++++++++++++++ .../faultinjection/TestFaultInjectUtil.java | 73 +++ handlers/pom.xml | 1 + java-chassis-dependencies/pom.xml | 5 + java-chassis-distribution/pom.xml | 4 + 21 files changed, 1629 insertions(+), 24 deletions(-) diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index 27a6c0e..337ae26 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -72,6 +72,10 @@ </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> + <artifactId>handler-fault-injection</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> <artifactId>common-rest</artifactId> </dependency> <dependency> diff --git a/handlers/handler-fault-injection/pom.xml b/handlers/handler-fault-injection/pom.xml new file mode 100755 index 0000000..3fda7b4 --- /dev/null +++ b/handlers/handler-fault-injection/pom.xml @@ -0,0 +1,44 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.servicecomb</groupId> + <artifactId>handlers</artifactId> + <version>1.0.0-m2-SNAPSHOT</version> + </parent> + <artifactId>handler-fault-injection</artifactId> + <name>Java Chassis::Handlers::Fault Injection</name> + <dependencies> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>java-chassis-core</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java new file mode 100644 index 0000000..3bf4d84 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbortFault.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class AbortFault extends AbstractFault { + private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionConfig.class); + + @Override + public FaultResponse injectFault(Invocation invocation, FaultParam faultParam) { + // get the config values related to delay. + int abortPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "abort.percent"); + + if (abortPercent == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) { + LOGGER.info("Fault injection: Abort percentage is not configured"); + return new FaultResponse(); + } + + // check fault abort condition. + boolean isAbort = FaultInjectionUtil.checkFaultInjectionDelayAndAbort(faultParam.getReqCount(), abortPercent); + if (isAbort) { + // get the config values related to delay percentage. + int errorCode = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "abort.httpStatus"); + + if (errorCode == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) { + LOGGER.info("Fault injection: Abort error code is not configured"); + return new FaultResponse(); + } + // if request need to be abort then return failure with given error code + CommonExceptionData errorData = new CommonExceptionData("aborted by fault inject"); + return new FaultResponse(-1, errorCode, errorData); + } + + return new FaultResponse(); + } + + @Override + public int getPriority() { + return FaultInjectionConst.FAULTINJECTION_PRIORITY_MIN; + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java new file mode 100644 index 0000000..db88a5e --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/AbstractFault.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import org.springframework.stereotype.Component; + +@Component +public abstract class AbstractFault implements Fault { + +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java new file mode 100644 index 0000000..756ad4b --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/BeanHolder.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.inject.Inject; + +import org.springframework.stereotype.Component; + +@Component +public class BeanHolder { + + @Inject + private List<Fault> faultInjectionFeatureList = Collections.emptyList(); + + public void init() { + //sort the fault injection feature list based on priority. + Collections.sort(faultInjectionFeatureList, new Comparator<Fault>() { + + @Override + public int compare(Fault o1, Fault o2) { + return Integer.compare(o1.getPriority(), o2.getPriority()); + } + }); + + FaultInjectionHandler.getFaultFeature().addAll(faultInjectionFeatureList); + + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java new file mode 100644 index 0000000..5ab0ed9 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/DelayFault.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import java.util.concurrent.CountDownLatch; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import io.vertx.core.Handler; +import io.vertx.core.Vertx; + +@Component +public class DelayFault extends AbstractFault { + private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionHandler.class); + + @Override + public int getPriority() { + return FaultInjectionConst.FAULTINJECTION_PRIORITY_MAX; + } + + @Override + public FaultResponse injectFault(Invocation invocation, FaultParam faultAttributes) { + int delayPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "delay.percent"); + + if (delayPercent == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) { + LOGGER.info("Fault injection: delay percentage is not configured"); + return new FaultResponse(); + } + + // check fault delay condition. + boolean isDelay = FaultInjectionUtil.checkFaultInjectionDelayAndAbort(faultAttributes.getReqCount(), delayPercent); + if (isDelay) { + LOGGER.info("Fault injection: delay is added for the request by fault inject handler"); + long delay = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "delay.fixedDelay"); + + if (delay == FaultInjectionConst.FAULT_INJECTION_CFG_NULL) { + LOGGER.info("Fault injection: delay is not configured"); + return new FaultResponse(); + } + + CountDownLatch latch = new CountDownLatch(1); + Vertx vertx = VertxUtils.getOrCreateVertxByName("faultinjection", null); + vertx.setTimer(delay, new Handler<Long>() { + @Override + public void handle(Long timeID) { + latch.countDown(); + } + }); + + try { + latch.await(); + } catch (InterruptedException e) { + LOGGER.info("Interrupted exception is received"); + } + } + + return new FaultResponse(); + } + +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java new file mode 100644 index 0000000..9d17e07 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/Fault.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import org.apache.servicecomb.core.Invocation; + +public interface Fault { + + int getPriority(); + + FaultResponse injectFault(Invocation invocation, FaultParam faultAttributes); +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java new file mode 100755 index 0000000..f6000e5 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfig.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.config.DynamicIntProperty; +import com.netflix.config.DynamicPropertyFactory; + +/** + * Handles the fault injection configuration read from micro service file/config + * center. + */ +public final class FaultInjectionConfig { + + private static final Logger LOGGER = LoggerFactory.getLogger(FaultInjectionConfig.class); + + // key is configuration parameter. + private static Map<String, String> cfgCallback = new ConcurrentHashMapEx<>(); + + public static int getConfigVal(String config, int defaultValue) { + DynamicIntProperty dynamicIntProperty = DynamicPropertyFactory.getInstance().getIntProperty(config, + defaultValue); + + if (cfgCallback.get(config) == null) { + cfgCallback.put(config, config); + dynamicIntProperty.addCallback(() -> { + int newValue = dynamicIntProperty.get(); + String cfgName = dynamicIntProperty.getName(); + + //store the value in config center map and check for next requests. + FaultInjectionUtil.setConfigCenterValue(cfgName, new AtomicInteger(newValue)); + LOGGER.info("{} changed to {}", cfgName, newValue); + }); + } + + return dynamicIntProperty.get(); + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java new file mode 100644 index 0000000..fdc2a8b --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +/** + * Handles the all constant values for fault injection. + */ +public class FaultInjectionConst { + + public static final int FAULT_INJECTION_CFG_NULL = -1; + + public static final String CONSUMER_FAULTINJECTION = "cse.governance.Consumer."; + + public static final String CONSUMER_FAULTINJECTION_GLOBAL = "cse.governance.Consumer._global."; + + public static final String CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS = "policy.fault.protocols."; + + public static final int FAULTINJECTION_PRIORITY_MIN = 10; + + public static final int FAULTINJECTION_PRIORITY_MAX = 1; +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java new file mode 100755 index 0000000..090b5a9 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionHandler.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.servicecomb.core.Handler; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.AsyncResponse; +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +/** + * Fault injection handler which injects the delay/abort for requests based on + * the percentage configured in service file. + * + */ + +public class FaultInjectionHandler implements Handler { + + private static List<Fault> faultInjectionFeatureList = new ArrayList<>(); + + //added only for unit testing + public void setFaultFeature(List<Fault> faultFeature) { + faultInjectionFeatureList = faultFeature; + } + + public static List<Fault> getFaultFeature() { + return faultInjectionFeatureList; + } + + @Override + public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { + + // prepare the key and lookup for request count. + String key = invocation.getTransport().getName() + invocation.getMicroserviceQualifiedName(); + AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key); + long reqCountCurrent = reqCount.get(); + + // increment the request count here after checking the delay/abort condition. + reqCount.incrementAndGet(); + + for (Fault fault : faultInjectionFeatureList) { + FaultResponse faultResponse = fault.injectFault(invocation, new FaultParam(reqCountCurrent)); + + if (faultResponse.getStatusCode() != 0) { + asyncResp.consumerFail(new InvocationException(new HttpStatus(faultResponse.getErrorCode(), + "invocation exception induced by fault injection"), faultResponse.getErrorData())); + return; + } + } + + invocation.next(asyncResp); + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java new file mode 100755 index 0000000..2ff4abf --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.FAULT_INJECTION_CFG_NULL; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +/** + * Handles the count for all request based key[transport + microservice qualified name]. + */ +public class FaultInjectionUtil { + + // key is transport+operQualifiedName + private static Map<String, AtomicLong> requestCount = new ConcurrentHashMapEx<>(); + + // key is config paramter + private static Map<String, AtomicInteger> configCenterValue = new ConcurrentHashMapEx<>(); + + /** + * Returns total requests per provider for operational level. + * + * @param key + * transport+operational name + * @return long total requests + */ + public static AtomicLong getOperMetTotalReq(String key) { + return requestCount.computeIfAbsent(key, p -> new AtomicLong(1)); + } + + /** + * Returns the map of config parameter key and values. + * @return value of config parameter map + */ + public static Map<String, AtomicInteger> getConfigCenterMap() { + return configCenterValue; + } + + /** + * Sets the value for given config parameter. + * @param key + * @param value + */ + public static void setConfigCenterValue(String key, AtomicInteger value) { + configCenterValue.put(key, value); + } + + /** + * Handles the reading fault injection configuration. + * + * @param invocation + * invocation of request + * @param key + * configuration key + * @return configuration value + */ + public static int getFaultInjectionConfig(Invocation invocation, String key) { + int value = 0; + String config; + + // get the config base on priority. operationName-->schema-->service-->global + String operationName = invocation.getOperationName(); + String schema = invocation.getSchemaId(); + String serviceName = invocation.getMicroserviceName(); + + config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + ".operations." + operationName + "." + + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + if ((value != FAULT_INJECTION_CFG_NULL)) { + return value; + } + + config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + "." + + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + if ((value != FAULT_INJECTION_CFG_NULL)) { + return value; + } + + config = CONSUMER_FAULTINJECTION + serviceName + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + + invocation.getTransport().getName() + "." + key; + value = getConfigValue(config); + if ((value != FAULT_INJECTION_CFG_NULL)) { + return value; + } + + config = CONSUMER_FAULTINJECTION_GLOBAL + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + return value; + } + + /** + * Get the configuration value + * @param config config parameter + * @return int value + */ + private static int getConfigValue(String config) { + int value = 0; + //first need to check in config center map which has high priority. + Map<String, AtomicInteger> cfgMap = FaultInjectionUtil.getConfigCenterMap(); + + if (cfgMap.containsKey(config)) { + return cfgMap.get(config).get(); + } + + value = FaultInjectionConfig.getConfigVal(config, FAULT_INJECTION_CFG_NULL); + return value; + + } + + /** + * It will check the delay/abort condition based on request count and percentage + * received. + * + * @param reqCount + * @param percentage + * @param key + * @return true/false + */ + public static boolean checkFaultInjectionDelayAndAbort(long reqCount, int percentage) { + /* + * Example: delay/abort percentage configured is 10% and Get the count(suppose + * if it is 10th request) from map and calculate resultNew(10th request) and + * requestOld(9th request). Like this for every request it will calculate + * current request count and previous count. if both not matched need to add + * delay/abort otherwise no need to add. + */ + + // calculate the value with current request count. + long resultNew = (reqCount * percentage) / 100; + + // calculate the value with previous count value. + long resultOld = ((reqCount - 1) * percentage) / 100; + + // if both are not matching then delay/abort should be added. + if (resultNew != resultOld) { + return true; + } + return false; + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java new file mode 100644 index 0000000..0dcd19b --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +/** + * Fault injection parameters which decides the fault injection condition. + */ +public class FaultParam { + private long reqCount; + + public long getReqCount() { + return reqCount; + } + + public void setReqCount(long reqCount) { + this.reqCount = reqCount; + } + + FaultParam(long reqCount) { + this.reqCount = reqCount; + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.java new file mode 100644 index 0000000..ee59d71 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultResponse.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +/** + * Fault response which contains status of fault injection. + */ +public class FaultResponse { + private int statusCode; + + private int errorCode; + + private Object errorData; + + FaultResponse(int statusCode, int errorCode, Object errorData) { + this.statusCode = statusCode; + this.errorCode = errorCode; + this.errorData = errorData; + } + + FaultResponse() { + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public Object getErrorData() { + return errorData; + } + + public void setErrorData(Object errorData) { + this.errorData = errorData; + } + +} diff --git a/handlers/pom.xml b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml old mode 100644 new mode 100755 similarity index 51% copy from handlers/pom.xml copy to handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml index 2afaa77..b8f5c57 --- a/handlers/pom.xml +++ b/handlers/handler-fault-injection/src/main/resources/META-INF/spring/cse.bean.xml @@ -16,27 +16,16 @@ ~ limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <parent> - <groupId>org.apache.servicecomb</groupId> - <artifactId>java-chassis-parent</artifactId> - <version>1.0.0-m2-SNAPSHOT</version> - <relativePath>../parent</relativePath> - </parent> - <modelVersion>4.0.0</modelVersion> - - <artifactId>handlers</artifactId> - <name>Java Chassis::Handlers</name> - <packaging>pom</packaging> - - <modules> - <module>handler-tracing-zipkin</module> - <module>handler-bizkeeper</module> - <module>handler-flowcontrol-qps</module> - <module>handler-loadbalance</module> - <module>handler-publickey-auth</module> - </modules> - -</project> + <beans xmlns = "http://www.springframework.org/schema/beans" + xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" + xmlns:context = "http://www.springframework.org/schema/context" + xsi:schemaLocation = "http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + <context:annotation-config /> + <!-- <context:spring-configured /> --> + <context:component-scan base-package="org.apache.servicecomb.faultinjection" /> + <bean class="org.apache.servicecomb.faultinjection.BeanHolder" init-method="init"/> +</beans> diff --git a/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml b/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml new file mode 100755 index 0000000..477963b --- /dev/null +++ b/handlers/handler-fault-injection/src/main/resources/config/cse.handler.xml @@ -0,0 +1,21 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You 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. + --> + +<config> + <handler id="fault-injection" + class="org.apache.servicecomb.faultinjection.FaultInjectionHandler" /> +</config> diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java new file mode 100644 index 0000000..f4d3137 --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectConfig.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests the fault injection configuration. + */ +public class TestFaultInjectConfig { + FaultInjectionConfig faultCfg; + + FaultInjectionConst faultConst; + + FaultInjectionUtil faultUtil; + + FaultParam faultParam; + + FaultResponse faultResp; + + AbortFault abortFault; + + DelayFault delayFault; + + @Before + public void setUp() throws Exception { + faultCfg = new FaultInjectionConfig(); + faultConst = new FaultInjectionConst(); + faultUtil = new FaultInjectionUtil(); + faultParam = new FaultParam(10); + faultResp = new FaultResponse(); + abortFault = new AbortFault(); + delayFault = new DelayFault(); + } + + @After + public void tearDown() throws Exception { + faultCfg = null; + faultConst = null; + faultUtil = null; + faultParam = null; + } + + @Test + public void testFaultInjectConfig() throws Exception { + int val = FaultInjectionConfig.getConfigVal("cse.servicecomb.handler.consumer.faultinject.config", 0); + Assert.assertEquals(0, val); + } + + @Test + public void testConstants() { + assertEquals("cse.governance.Consumer.", FaultInjectionConst.CONSUMER_FAULTINJECTION); + assertEquals("policy.fault.protocols.", FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS); + assertEquals(-1, FaultInjectionConst.FAULT_INJECTION_CFG_NULL); + assertEquals("cse.governance.Consumer._global.", FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL); + assertEquals(10, FaultInjectionConst.FAULTINJECTION_PRIORITY_MIN); + assertEquals(1, FaultInjectionConst.FAULTINJECTION_PRIORITY_MAX); + } + + @Test + public void testFaultParam() { + faultParam.setReqCount(100); + assertEquals(100, faultParam.getReqCount()); + } + + @Test + public void testFaultResponse() { + Object obj = new Object(); + faultResp.setErrorCode(100); + faultResp.setErrorData(obj); + faultResp.setStatusCode(123); + assertEquals(123, faultResp.getStatusCode()); + assertEquals(100, faultResp.getErrorCode()); + assertEquals(obj, faultResp.getErrorData()); + } + + @Test + public void testFaultPriority() { + assertEquals(10, abortFault.getPriority()); + assertEquals(1, delayFault.getPriority()); + } +} diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java new file mode 100644 index 0000000..7288f78 --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectHandler.java @@ -0,0 +1,683 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.swagger.invocation.AsyncResponse; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** + * Tests the fault injection handler functionality. + */ +public class TestFaultInjectHandler { + FaultInjectionHandler handler; + + Invocation invocation; + + AsyncResponse asyncResp; + + OperationMeta operationMeta; + + private Transport transport; + + Response response; + + + @InjectMocks + FaultInjectionHandler faultHandler; + + @InjectMocks + AbortFault abortFault; + + @InjectMocks + DelayFault delayFault; + + AsyncResponse ar = new AsyncResponse() { + @Override + public void handle(Response resp) { + response = resp; + } + }; + + + @Before + public void setUp() throws Exception { + handler = new FaultInjectionHandler(); + + invocation = Mockito.mock(Invocation.class); + + asyncResp = Mockito.mock(AsyncResponse.class); + + operationMeta = Mockito.mock(OperationMeta.class); + + transport = Mockito.mock(Transport.class); + MockitoAnnotations.initMocks(this); + } + + @After + public void tearDown() throws Exception { + handler = null; + + invocation = null; + + asyncResp = null; + + operationMeta = null; + + transport = null; + } + + /** + * Tests the fault injection handler functionality with default values for + * highway transport. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerHighwayWithDefaultCfg() throws Exception { + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName1"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("highway"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHello"); + Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("hello"); + + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + handler.handle(invocation, asyncResp); + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("highwayMicroserviceQualifiedName1"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with default values for rest + * transport. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerRestWithDefaultCfg() throws Exception { + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName2"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHello"); + Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("hello"); + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + handler.handle(invocation, asyncResp); + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName2"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with global configuration + * with delay/abort condition. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeGlobal() throws Exception { + + System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", "5"); + System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", "10"); + System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", "10"); + System.setProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName3"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHello"); + Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("hello"); + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + boolean validAssert; + try { + validAssert = true; + handler.handle(invocation, asyncResp); + } catch (Exception e) { + validAssert = false; + } + + System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent"); + System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent"); + System.getProperties().remove("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus"); + + Assert.assertTrue(validAssert); + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName3"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with service level configuration + * with delay/abort condition. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerServiceCfgSuccess() throws Exception { + + System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.fixedDelay", "1"); + System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.percent", "10"); + System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.percent", "10"); + System.setProperty("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.httpStatus", "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName4"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHello"); + Mockito.when(invocation.getSchemaId()).thenReturn("sayHelloSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts"); + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + boolean validAssert; + try { + validAssert = true; + handler.handle(invocation, asyncResp); + } catch (Exception e) { + validAssert = false; + } + + System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.delay.percent"); + System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.percent"); + System.getProperties().remove("cse.governance.Consumer.carts.policy.fault.protocols.rest.abort.httpStatus"); + + Assert.assertTrue(validAssert); + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName4"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with schema level configuration + * with delay/abort condition. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerSchemaCfgSuccess() throws Exception { + + System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.fixedDelay", + "1"); + System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.percent", + "10"); + System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.percent", + "10"); + System.setProperty("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.httpStatus", + "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName5"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHello"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts"); + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + boolean validAssert; + try { + validAssert = true; + handler.handle(invocation, asyncResp); + } catch (Exception e) { + validAssert = false; + } + + System.getProperties() + .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts.schemas.testSchema.policy.fault.protocols.rest.abort.httpStatus"); + + Assert.assertTrue(validAssert); + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName5"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with operation level configuration + * with delay/abort condition. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerOperationCfgSuccess() throws Exception { + + System.setProperty( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.fixedDelay", + "1"); + System.setProperty( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.percent", + "10"); + System.setProperty( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.percent", + "10"); + System.setProperty( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.httpStatus", + "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName6"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayHi"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts"); + + List<Fault> faultInjectionFeatureList = Arrays.asList(abortFault, delayFault); + handler.setFaultFeature(faultInjectionFeatureList); + + boolean validAssert; + try { + validAssert = true; + handler.handle(invocation, asyncResp); + } catch (Exception e) { + validAssert = false; + } + + System.getProperties() + .remove( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts.schemas.testSchema.operations.sayHi.policy.fault.protocols.rest.abort.httpStatus"); + Assert.assertTrue(validAssert); + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName6"); + assertEquals(2, count.get()); + } + + /** + * Tests the fault injection handler functionality with configuration change event for global level config. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeEvent1() throws Exception { + + System.setProperty( + "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", + "1"); + System.setProperty( + "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent", + "100"); + System.setProperty( + "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent", + "100"); + System.setProperty( + "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", + "420"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName7"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye1"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema1"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts1"); + boolean validAssert; + long timeOld = System.currentTimeMillis(); + + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + handler.setFaultFeature(faultInjectionFeatureList); + + try { + validAssert = true; + handler.handle(invocation, ar); + } catch (Exception e) { + validAssert = false; + } + Assert.assertTrue(validAssert); + TestFaultInjectUtil + .updateProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay", 500); + TestFaultInjectUtil + .updateProperty("cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus", 421); + + handler.handle(invocation, ar -> { + //check whether error code return, defaut is 421. + assertEquals(421, response.getStatusCode()); + assertEquals(true, response.isFailed()); + long timeNow = System.currentTimeMillis(); + //if really time delay is added it should be greater than 5s. + Assert.assertTrue((timeNow - timeOld) >= 500); + }); + + + System.getProperties() + .remove( + "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove( + "cse.governance.Consumer._global.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer._global.policy.fault.protocols.rest.abort.httpStatus"); + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName7"); + assertEquals(3, count.get()); + + } + + /** + * Tests the fault injection handler functionality with configuration change event for operation level config. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeEvent2() throws Exception { + + System.setProperty( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay", + "1"); + System.setProperty( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.httpStatus", + "420"); + + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName8"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye2"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema2"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts2"); + boolean validAssert; + long timeOld = System.currentTimeMillis(); + + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + handler.setFaultFeature(faultInjectionFeatureList); + + try { + validAssert = true; + handler.handle(invocation, ar); + } catch (Exception e) { + validAssert = false; + } + Assert.assertTrue(validAssert); + TestFaultInjectUtil + .updateProperty( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay", + 500); + + handler.handle(invocation, ar -> { + //check whether error code return + assertEquals(420, response.getStatusCode()); + assertEquals(true, response.isFailed()); + long timeNow = System.currentTimeMillis(); + //if really time delay is added it should be greater than 5s. + Assert.assertTrue((timeNow - timeOld) >= 500); + }); + + System.getProperties() + .remove( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove( + "cse.governance.Consumer.carts2.schemas.testSchema2.operations.sayBye2.policy.fault.protocols.rest.abort.httpStatus"); + + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName8"); + assertEquals(3, count.get()); + + } + + /** + * Tests the fault injection handler functionality with configuration change event for schema level config. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeEvent3() throws Exception { + + System.setProperty( + "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay", + "1"); + System.setProperty( + "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.httpStatus", + "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName9"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye3"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema3"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts3"); + boolean validAssert; + long timeOld = System.currentTimeMillis(); + + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + handler.setFaultFeature(faultInjectionFeatureList); + + try { + validAssert = true; + handler.handle(invocation, ar); + } catch (Exception e) { + validAssert = false; + } + Assert.assertTrue(validAssert); + TestFaultInjectUtil + .updateProperty( + "cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay", + 500); + + handler.handle(invocation, ar -> { + //check whether error code return, defaut is 421. + assertEquals(421, response.getStatusCode()); + assertEquals(true, response.isFailed()); + long timeNow = System.currentTimeMillis(); + //if really time delay is added it should be greater than 5s. + Assert.assertTrue((timeNow - timeOld) >= 500); + }); + + System.getProperties() + .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts3.schemas.testSchema3.policy.fault.protocols.rest.abort.httpStatus"); + + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName9"); + assertEquals(3, count.get()); + } + + /** + * Tests the fault injection handler functionality with configuration change event for service level config. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeEvent4() throws Exception { + System.setProperty("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay", "1"); + + System.setProperty( + "cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.httpStatus", + "421"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName10"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts4"); + boolean validAssert; + long timeOld = System.currentTimeMillis(); + + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + handler.setFaultFeature(faultInjectionFeatureList); + + try { + validAssert = true; + handler.handle(invocation, ar); + } catch (Exception e) { + validAssert = false; + } + Assert.assertTrue(validAssert); + TestFaultInjectUtil + .updateProperty("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay", 500); + + handler.handle(invocation, ar -> { + //check whether error code return, + assertEquals(421, response.getStatusCode()); + assertEquals(true, response.isFailed()); + long timeNow = System.currentTimeMillis(); + //if really time delay is added it should be greater than 5s. + Assert.assertTrue((timeNow - timeOld) >= 500); + }); + + + System.getProperties() + .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts4.policy.fault.protocols.rest.abort.httpStatus"); + + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName10"); + assertEquals(3, count.get()); + + } + + /** + * Tests the fault injection handler functionality with configuration change event for service level config. + * + * @throws Exception + */ + @Test + public void testFaultInjectHandlerConfigChangeEvent5() throws Exception { + System.setProperty( + "cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.percent", + "100"); + System.setProperty( + "cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent", + "100"); + + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName11"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts5"); + boolean validAssert; + long timeOld = System.currentTimeMillis(); + + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + handler.setFaultFeature(faultInjectionFeatureList); + + try { + validAssert = true; + handler.handle(invocation, ar); + } catch (Exception e) { + validAssert = false; + } + Assert.assertTrue(validAssert); + TestFaultInjectUtil + .updateProperty("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent", 500); + + handler.handle(invocation, ar -> { + //check whether error code return, + assertEquals(421, response.getStatusCode()); + assertEquals(true, response.isFailed()); + long timeNow = System.currentTimeMillis(); + //if really time delay is added it should be greater than 5s. + Assert.assertTrue((timeNow - timeOld) >= 500); + }); + + + System.getProperties() + .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.fixedDelay"); + System.getProperties() + .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.delay.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.percent"); + System.getProperties() + .remove("cse.governance.Consumer.carts5.policy.fault.protocols.rest.abort.httpStatus"); + + + AtomicLong count = FaultInjectionUtil.getOperMetTotalReq("restMicroserviceQualifiedName11"); + assertEquals(3, count.get()); + + } +} diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java new file mode 100644 index 0000000..46b428e --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.servicecomb.faultinjection; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.util.ReflectionUtils; + +import com.netflix.config.DynamicProperty; + +/** + * Tests the fault inject util functionality. + */ +public class TestFaultInjectUtil { + private static Method updatePropertyMethod = + ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); + + static { + updatePropertyMethod.setAccessible(true); + } + + DelayFault delayFault = Mockito.mock(DelayFault.class); + + AbortFault abortFault = Mockito.mock(AbortFault.class); + + public static void updateProperty(String key, Object value) { + ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); + } + + @Test + public void testFaultInjectUtil() throws Exception { + BeanHolder beanHolder = new BeanHolder(); + List<Fault> faultInjectionFeatureList = Arrays.asList(delayFault, abortFault); + Field field = beanHolder.getClass().getDeclaredField("faultInjectionFeatureList"); + field.setAccessible(true); + field.set(beanHolder, faultInjectionFeatureList); + Mockito.when(delayFault.getPriority()).thenReturn(1); + Mockito.when(abortFault.getPriority()).thenReturn(10); + + beanHolder.init(); + AtomicLong count1 = FaultInjectionUtil.getOperMetTotalReq("test"); + Assert.assertEquals(1, count1.get()); + count1.incrementAndGet(); + AtomicLong count2 = FaultInjectionUtil.getOperMetTotalReq("test"); + Assert.assertEquals(2, count2.get()); + FaultInjectionUtil.setConfigCenterValue("sayHi", new AtomicInteger(123)); + int value = FaultInjectionUtil.getConfigCenterMap().get("sayHi").get(); + Assert.assertEquals(123, value); + } +} diff --git a/handlers/pom.xml b/handlers/pom.xml index 2afaa77..a16e93e 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -36,6 +36,7 @@ <module>handler-bizkeeper</module> <module>handler-flowcontrol-qps</module> <module>handler-loadbalance</module> + <module>handler-fault-injection</module> <module>handler-publickey-auth</module> </modules> diff --git a/java-chassis-dependencies/pom.xml b/java-chassis-dependencies/pom.xml index 5793296..e9ae9ec 100644 --- a/java-chassis-dependencies/pom.xml +++ b/java-chassis-dependencies/pom.xml @@ -836,6 +836,11 @@ </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> + <artifactId>handler-fault-injection</artifactId> + <version>1.0.0-m2-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> <artifactId>common-rest</artifactId> <version>1.0.0-m2-SNAPSHOT</version> </dependency> diff --git a/java-chassis-distribution/pom.xml b/java-chassis-distribution/pom.xml index 1b7f364..95b30e1 100644 --- a/java-chassis-distribution/pom.xml +++ b/java-chassis-distribution/pom.xml @@ -129,6 +129,10 @@ <artifactId>handler-tracing-zipkin</artifactId> </dependency> <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>handler-fault-injection</artifactId> + </dependency> + <dependency> <groupId>org.apache.servicecomb</groupId> <artifactId>provider-jaxrs</artifactId> </dependency> -- To stop receiving notification emails like this one, please contact [email protected].
