├── simianarmy ├── src │ ├── main │ │ ├── resources │ │ │ ├── scripts │ │ │ │ ├── killprocess │ │ │ │ ├── burncpu │ │ │ │ ├── nullroute │ │ │ │ ├── burnio │ │ │ │ ├── filldisk │ │ │ │ └── blockport │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── simianarmy │ │ │ ├── client │ │ │ ├── BurnIoSimianArmyChaosScript.java │ │ │ ├── NullRouteSimianArmyChaosScript.java │ │ │ ├── BurnCpuSimianArmyChaosScript.java │ │ │ ├── FillDiskSimianArmyChaosScript.java │ │ │ ├── KillProcessSimianArmyChaosScript.java │ │ │ ├── BlockPortSimianArmyChaosScript.java │ │ │ └── SimianArmyScriptChaos.java │ │ │ ├── QOperativeSystemChaosSimianArmyExtension.java │ │ │ ├── QOperativeSystemChaosSimianArmy.java │ │ │ ├── QOperativeSystemChaosSimianArmyResourceProvider.java │ │ │ ├── QOperativeSystemChaosSimianArmyCreator.java │ │ │ └── QSimianArmyAction.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillia │ │ └── cube │ │ └── q │ │ └── simianarmy │ │ └── client │ │ └── SimianArmyScriptChaosTest.java └── pom.xml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── core ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── core │ │ │ ├── QExtension.java │ │ │ ├── InstallProxy.java │ │ │ ├── InstallStandaloneContainer.java │ │ │ └── RegisterProxy.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── InstallProxyTestCase.java └── pom.xml ├── pumba ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── pumba │ │ │ ├── QContainerChaosPumbaExtension.java │ │ │ ├── QContainerChaosPumbaResourceProvider.java │ │ │ ├── QContainerChaosPumba.java │ │ │ ├── QContainerChaosPumbaStopper.java │ │ │ ├── QContainerChaosPumbaCreator.java │ │ │ ├── PumbaStandaloneContainerHandler.java │ │ │ ├── util │ │ │ └── PumbaCommandLineCreator.java │ │ │ └── QPumbaAction.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── pumba │ │ ├── util │ │ └── PumbaCommandLineCreatorTest.java │ │ ├── PumbaChaosCommandBuilderTest.java │ │ └── PumbaStandaloneContainerHandlerTest.java └── pom.xml ├── toxic ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── toxic │ │ │ ├── ToxiProxyAfterTestCleaner.java │ │ │ ├── event │ │ │ ├── ToxicUpdated.java │ │ │ └── ToxicCreated.java │ │ │ ├── QNetworkChaosToxicExtension.java │ │ │ ├── QNetworkChaosResourceProvider.java │ │ │ ├── client │ │ │ ├── ToxiProxy.java │ │ │ ├── ToxiProxyContainer.java │ │ │ ├── NetworkChaosConfigurator.java │ │ │ └── ToxiProxyScenario.java │ │ │ ├── QNetworkChaosToxicCreator.java │ │ │ ├── ToxicProxyHandler.java │ │ │ └── QNetworkChaosToxic.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── toxic │ │ └── InstallProxyTestCase.java └── pom.xml ├── reporter ├── src │ ├── main │ │ ├── resources │ │ │ ├── org.arquillian.cube.q.recorder.NetworkChaosInformationReportKey.properties │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── org.jboss.arquillian.core.spi.LoadableExtension │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── recorder │ │ │ ├── NetworkChaosInformationReportKey.java │ │ │ ├── CubeQReporterExtension.java │ │ │ └── TakeNetworkChaosInformation.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── recorder │ │ └── TakeNetworkChaosInformationTest.java └── pom.xml ├── ftest-toxic ├── src │ └── test │ │ ├── resources │ │ ├── server2 │ │ │ └── Dockerfile │ │ ├── server1 │ │ │ └── Dockerfile │ │ └── arquillian.xml │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── toxic │ │ └── ToxicFunctionalTestCase.java └── pom.xml ├── ftest-toxic-frontend ├── src │ └── test │ │ ├── resources │ │ ├── server1 │ │ │ └── Dockerfile │ │ ├── server2 │ │ │ └── Dockerfile │ │ └── arquillian.xml │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── toxic │ │ └── ToxicFunctionalTestCase.java └── pom.xml ├── spi ├── src │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── spi │ │ │ ├── StandaloneManager.java │ │ │ ├── ProxyManager.java │ │ │ ├── NetworkChaosConfiguration.java │ │ │ ├── StandaloneContainer.java │ │ │ └── Proxy.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── spi │ │ └── ProxyBuilderTestCase.java └── pom.xml ├── api ├── src │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── arquillian │ │ │ └── cube │ │ │ └── q │ │ │ └── api │ │ │ ├── Distribution.java │ │ │ ├── UniformDistribution.java │ │ │ ├── LogNormalDistribution.java │ │ │ ├── ContainerChaos.java │ │ │ ├── OperativeSystemChaos.java │ │ │ └── Q.java │ └── test │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── api │ │ └── QRunConditionTest.java └── pom.xml ├── .gitignore ├── .editorconfig ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── ftest-simianarmy ├── src │ └── test │ │ ├── resources │ │ └── arquillian.xml │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── simianarmy │ │ └── SimianArmyFunctionalTestCase.java └── pom.xml ├── ftest-pumba ├── src │ └── test │ │ ├── resources │ │ └── arquillian.xml │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── pumba │ │ └── PumbaFunctionalTestCase.java └── pom.xml ├── ftest-toxic-reporter ├── src │ └── test │ │ ├── resources │ │ └── arquillian.xml │ │ └── java │ │ └── org │ │ └── arquillian │ │ └── cube │ │ └── q │ │ └── toxic │ │ └── reporter │ │ └── ToxicFunctionalTest.java └── pom.xml ├── .travis.yml ├── mvnw.cmd ├── mvnw ├── pom.xml └── LICENSE /simianarmy/src/main/resources/scripts/killprocess: -------------------------------------------------------------------------------- 1 | pkill -f ${process} -------------------------------------------------------------------------------- /simianarmy/src/main/resources/scripts/burncpu: -------------------------------------------------------------------------------- 1 | dd if=/dev/zero of=/dev/null -------------------------------------------------------------------------------- /simianarmy/src/main/resources/scripts/nullroute: -------------------------------------------------------------------------------- 1 | ip route add blackhole 10.0.0.0/8 -------------------------------------------------------------------------------- /simianarmy/src/main/resources/scripts/burnio: -------------------------------------------------------------------------------- 1 | dd if=/dev/urandom of=/burn bs=1M count=1024 iflag=fullblock -------------------------------------------------------------------------------- /simianarmy/src/main/resources/scripts/filldisk: -------------------------------------------------------------------------------- 1 | dd if=/dev/urandom of=/burn bs=1M count=${size} iflag=fullblock -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arquillian/arquillian-cube-q/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /core/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension: -------------------------------------------------------------------------------- 1 | org.arquillian.cube.q.core.QExtension -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip -------------------------------------------------------------------------------- /pumba/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension: -------------------------------------------------------------------------------- 1 | org.arquillian.cube.q.pumba.QContainerChaosPumbaExtension -------------------------------------------------------------------------------- /toxic/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension: -------------------------------------------------------------------------------- 1 | org.arquillian.cube.q.toxic.QNetworkChaosToxicExtension -------------------------------------------------------------------------------- /reporter/src/main/resources/org.arquillian.cube.q.recorder.NetworkChaosInformationReportKey.properties: -------------------------------------------------------------------------------- 1 | TOXICITY_DETAILS_PATH.value=Toxicity Details Path -------------------------------------------------------------------------------- /reporter/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension: -------------------------------------------------------------------------------- 1 | org.arquillian.cube.q.recorder.CubeQReporterExtension 2 | 3 | -------------------------------------------------------------------------------- /simianarmy/src/main/resources/scripts/blockport: -------------------------------------------------------------------------------- 1 | iptables -A INPUT -p tcp -m tcp --dport ${port} -j DROP 2 | iptables -A INPUT -p udp -m udp --dport ${port} -j DROP -------------------------------------------------------------------------------- /simianarmy/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension: -------------------------------------------------------------------------------- 1 | org.arquillian.cube.q.simianarmy.QOperativeSystemChaosSimianArmyExtension -------------------------------------------------------------------------------- /ftest-toxic/src/test/resources/server2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | RUN yum install -y nmap-ncat 4 | 5 | EXPOSE 80 6 | CMD nc -lk 0.0.0.0 80 -c 'curl -D - http://google.com' -------------------------------------------------------------------------------- /ftest-toxic/src/test/resources/server1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | RUN yum install -y nmap-ncat 4 | 5 | EXPOSE 8085 6 | CMD nc -lk 0.0.0.0 8085 -c 'curl -D - http://server2' -------------------------------------------------------------------------------- /ftest-toxic-frontend/src/test/resources/server1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | RUN yum install -y nmap-ncat 4 | 5 | EXPOSE 8085 6 | CMD nc -lk 0.0.0.0 8085 -c 'curl -D - http://server2' -------------------------------------------------------------------------------- /ftest-toxic-frontend/src/test/resources/server2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | RUN yum install -y nmap-ncat 4 | 5 | EXPOSE 80 6 | CMD nc -lk 0.0.0.0 80 -c 'curl -D - http://google.com' -------------------------------------------------------------------------------- /spi/src/main/java/org/arquillian/cube/q/spi/StandaloneManager.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | public interface StandaloneManager { 4 | 5 | StandaloneContainer install(); 6 | } 7 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/Distribution.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | /** 4 | * Base interface for implementing any kind of mathematical distribution 5 | */ 6 | public interface Distribution { 7 | 8 | long calculate(); 9 | } 10 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/BurnIoSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | public class BurnIoSimianArmyChaosScript extends SimianArmyScriptChaos { 4 | public BurnIoSimianArmyChaosScript() { 5 | super("burnio"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/NullRouteSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | public class NullRouteSimianArmyChaosScript extends SimianArmyScriptChaos { 4 | public NullRouteSimianArmyChaosScript() { 5 | super("nullroute"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /reporter/src/main/java/org/arquillian/cube/q/recorder/NetworkChaosInformationReportKey.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.recorder; 2 | 3 | import org.arquillian.reporter.api.model.AbstractStringKey; 4 | import org.arquillian.reporter.api.model.StringKey; 5 | 6 | public class NetworkChaosInformationReportKey extends AbstractStringKey { 7 | 8 | public static final StringKey TOXICITY_DETAILS_PATH = new NetworkChaosInformationReportKey(); 9 | } 10 | -------------------------------------------------------------------------------- /spi/src/main/java/org/arquillian/cube/q/spi/ProxyManager.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 4 | import org.arquillian.cube.spi.Cube; 5 | 6 | public interface ProxyManager { 7 | 8 | Proxy install(DockerCompositions containers); 9 | 10 | void proxyStarted(Cube cube); 11 | 12 | void populateProxies(); 13 | 14 | void cubeStopped(Cube cube); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/org/arquillian/cube/q/core/QExtension.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.core; 2 | 3 | import org.jboss.arquillian.core.spi.LoadableExtension; 4 | 5 | public class QExtension implements LoadableExtension { 6 | 7 | @Override 8 | public void register(ExtensionBuilder builder) { 9 | builder.observer(InstallProxy.class) 10 | .observer(RegisterProxy.class) 11 | .observer(InstallStandaloneContainer.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/ToxiProxyAfterTestCleaner.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.q.toxic.client.ToxiProxyScenario; 4 | import org.jboss.arquillian.core.api.annotation.Observes; 5 | import org.jboss.arquillian.test.spi.event.suite.After; 6 | 7 | public class ToxiProxyAfterTestCleaner { 8 | 9 | public void resetToxiProxiesAfterTest(@Observes After event, ToxiProxyScenario scenario) { 10 | scenario.reset(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .project 3 | .classpath 4 | .settings/ 5 | .factorypath 6 | 7 | # IntelliJ 8 | *.iml 9 | *.ipr 10 | *.iws 11 | .idea 12 | bin/ 13 | 14 | # Gradle 15 | .gradle/ 16 | .ivy/ 17 | 18 | # Maven 19 | target/ 20 | 21 | # TestNG 22 | test-output/ 23 | 24 | # JBoss AS 25 | transaction.log 26 | 27 | # Infinitest configuration 28 | infinitest.filters 29 | 30 | # Logs 31 | *.log 32 | *.log~ 33 | *.html 34 | # Libreoffice leftovers (XLS) 35 | .~lock* 36 | 37 | .DS_Store 38 | 39 | atlassian-ide-plugin.xml 40 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/event/ToxicUpdated.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.event; 2 | 3 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 4 | import org.jboss.arquillian.core.spi.event.Event; 5 | 6 | public class ToxicUpdated implements Event { 7 | 8 | private ToxiProxyClient.BaseToxic toxic; 9 | 10 | public ToxiProxyClient.BaseToxic getToxic() { 11 | return toxic; 12 | } 13 | 14 | public ToxicUpdated(ToxiProxyClient.BaseToxic toxic) { 15 | this.toxic = toxic; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset=utf-8 3 | end_of_line=lf 4 | insert_final_newline=true 5 | indent_style=space 6 | indent_size=4 7 | 8 | [{*.mod,*.dtd,*.ent,*.elt}] 9 | indent_style=space 10 | indent_size=2 11 | 12 | [{*.jhm,*.xjb,*.rng,*.wsdl,*.fxml,*.plan,*.pom,*.xslt,*.jrxml,*.ant,*.svg,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.wsdd,*.wadl,*.xml}] 13 | indent_style=space 14 | indent_size=2 15 | 16 | [{.babelrc,.stylelintrc,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] 17 | indent_style=space 18 | indent_size=2 19 | 20 | [{*.yml,*.yaml}] 21 | indent_style=space 22 | indent_size=2 23 | 24 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/UniformDistribution.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public class UniformDistribution implements Distribution { 6 | 7 | private long lower; 8 | private long upper; 9 | 10 | public UniformDistribution(long upper, long lower) { 11 | this.lower = lower; 12 | this.upper = upper; 13 | } 14 | 15 | @Override 16 | public long calculate() { 17 | return ThreadLocalRandom.current().nextLong(lower, upper + 1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | #### Short description of what this resolves: 13 | 14 | 15 | #### Changes proposed in this pull request: 16 | 17 | - 18 | - 19 | - 20 | 21 | 22 | **Fixes**: # 23 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/LogNormalDistribution.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public class LogNormalDistribution implements Distribution { 6 | 7 | private double sigma; 8 | private long median; 9 | 10 | public LogNormalDistribution(long median, double sigma) { 11 | this.sigma = sigma; 12 | this.median = median; 13 | } 14 | 15 | @Override 16 | public long calculate() { 17 | return Math.round(Math.exp(ThreadLocalRandom.current().nextGaussian() * sigma) * median); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ftest-simianarmy/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | dev 10 | 11 | pingpong: 12 | image: jonmorehouse/ping-pong 13 | portBindings: [8081->8080/tcp] 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /reporter/src/main/java/org/arquillian/cube/q/recorder/CubeQReporterExtension.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.recorder; 2 | 3 | import org.arquillian.reporter.api.model.StringKey; 4 | import org.jboss.arquillian.core.spi.LoadableExtension; 5 | 6 | public class CubeQReporterExtension implements LoadableExtension { 7 | 8 | @Override 9 | public void register(ExtensionBuilder builder) { 10 | if (Validate.classExists("org.arquillian.core.reporter.ArquillianCoreReporterExtension")) { 11 | builder.observer(TakeNetworkChaosInformation.class); 12 | builder.service(StringKey.class, NetworkChaosInformationReportKey.class); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/QOperativeSystemChaosSimianArmyExtension.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import org.jboss.arquillian.core.spi.LoadableExtension; 4 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 5 | 6 | public class QOperativeSystemChaosSimianArmyExtension implements LoadableExtension { 7 | 8 | @Override 9 | public void register(ExtensionBuilder extensionBuilder) { 10 | extensionBuilder 11 | .service(ResourceProvider.class, QOperativeSystemChaosSimianArmyResourceProvider.class) 12 | .observer(QOperativeSystemChaosSimianArmyCreator.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ftest-pumba/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | dev 10 | 11 | pingpong: 12 | image: jonmorehouse/ping-pong 13 | exposedPorts: [8080/tcp] 14 | 15 | pingpong2: 16 | image: jonmorehouse/ping-pong 17 | exposedPorts: [8080/tcp] 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QContainerChaosPumbaExtension.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.q.spi.StandaloneManager; 4 | import org.jboss.arquillian.core.spi.LoadableExtension; 5 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 6 | 7 | public class QContainerChaosPumbaExtension implements LoadableExtension { 8 | 9 | @Override 10 | public void register(ExtensionBuilder extensionBuilder) { 11 | extensionBuilder 12 | .service(StandaloneManager.class, PumbaStandaloneContainerHandler.class) 13 | .service(ResourceProvider.class, QContainerChaosPumbaResourceProvider.class) 14 | .observer(QContainerChaosPumbaCreator.class) 15 | .observer(QContainerChaosPumbaStopper.class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/BurnCpuSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public class BurnCpuSimianArmyChaosScript extends SimianArmyScriptChaos { 8 | 9 | private int numberCpu; 10 | 11 | public BurnCpuSimianArmyChaosScript(int numberCpu) { 12 | super("burncpu"); 13 | this.numberCpu = numberCpu; 14 | } 15 | 16 | @Override 17 | public String[] postProcessScript(String[] chaosScriptsContent) { 18 | List commands = new ArrayList<>(); 19 | 20 | for (int i = 0; i < numberCpu; i++) { 21 | Collections.addAll(commands, chaosScriptsContent); 22 | } 23 | 24 | return commands.toArray(new String[commands.size()]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/QNetworkChaosToxicExtension.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.q.spi.ProxyManager; 4 | import org.arquillian.cube.q.toxic.client.NetworkChaosConfigurator; 5 | import org.jboss.arquillian.core.spi.LoadableExtension; 6 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 7 | 8 | public class QNetworkChaosToxicExtension implements LoadableExtension { 9 | 10 | @Override 11 | public void register(ExtensionBuilder builder) { 12 | builder.observer(QNetworkChaosToxicCreator.class) 13 | .observer(NetworkChaosConfigurator.class) 14 | .observer(ToxiProxyAfterTestCleaner.class) 15 | .service(ProxyManager.class, ToxicProxyHandler.class) 16 | .service(ResourceProvider.class, QNetworkChaosResourceProvider.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spi/src/main/java/org/arquillian/cube/q/spi/NetworkChaosConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | import java.util.Map; 4 | 5 | public class NetworkChaosConfiguration { 6 | 7 | private static final String TOXIFY_PORT_BINDING = "toxifyPortBinding"; 8 | 9 | private boolean toxifyPortBinding = false; 10 | 11 | public boolean isToxifyPortBinding() { 12 | return toxifyPortBinding; 13 | } 14 | 15 | public static NetworkChaosConfiguration fromMap(Map configuration) { 16 | 17 | NetworkChaosConfiguration networkChaosConfiguration = new NetworkChaosConfiguration(); 18 | 19 | if (configuration.containsKey(TOXIFY_PORT_BINDING)) { 20 | networkChaosConfiguration.toxifyPortBinding = Boolean.parseBoolean(configuration.get(TOXIFY_PORT_BINDING)); 21 | } 22 | 23 | return networkChaosConfiguration; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/QNetworkChaosResourceProvider.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import java.lang.annotation.Annotation; 4 | 5 | import org.arquillian.cube.q.api.NetworkChaos; 6 | import org.jboss.arquillian.core.api.Instance; 7 | import org.jboss.arquillian.core.api.annotation.Inject; 8 | import org.jboss.arquillian.test.api.ArquillianResource; 9 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 10 | 11 | public class QNetworkChaosResourceProvider implements ResourceProvider { 12 | 13 | @Inject 14 | private Instance networkChaosInst; 15 | 16 | public boolean canProvide(Class type) { 17 | return type.isAssignableFrom(NetworkChaos.class); 18 | } 19 | 20 | public Object lookup(ArquillianResource resource, Annotation... qualifiers) { 21 | return networkChaosInst.get(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/event/ToxicCreated.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.event; 2 | 3 | import org.arquillian.cube.q.api.Q; 4 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 5 | import org.jboss.arquillian.core.spi.event.Event; 6 | 7 | public class ToxicCreated implements Event { 8 | 9 | private ToxiProxyClient.BaseToxic toxic; 10 | 11 | private Q.RunCondition runCondition; 12 | 13 | public ToxicCreated(ToxiProxyClient.BaseToxic toxic) { 14 | this.toxic = toxic; 15 | } 16 | 17 | public ToxicCreated(ToxiProxyClient.BaseToxic toxic, Q.RunCondition runCondition) { 18 | this.toxic = toxic; 19 | this.runCondition = runCondition; 20 | } 21 | 22 | public ToxiProxyClient.BaseToxic getToxic() { 23 | return toxic; 24 | } 25 | 26 | public Q.RunCondition getRunCondition() { 27 | return runCondition; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/client/ToxiProxy.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.client; 2 | 3 | import org.arquillian.cube.q.api.Q; 4 | 5 | import java.util.List; 6 | 7 | public interface ToxiProxy { 8 | 9 | void register(String name, String listen, String upstream); 10 | 11 | Scenario given(String name); 12 | 13 | interface Scenario { 14 | 15 | Scenario given(String name); 16 | 17 | Scenario using(List toxic); 18 | 19 | void then(Callable callable) throws Exception; 20 | 21 | void then(Q.RunCondition runCondition, Callable callable) throws Exception; 22 | 23 | void execute() throws Exception; 24 | 25 | void execute(Q.RunCondition runCondition) throws Exception; 26 | 27 | void update() throws Exception; 28 | } 29 | 30 | interface Callable { 31 | void call() throws Exception; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ftest-toxic/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | dev 10 | CUBE 11 | 12 | hw: 13 | image: lordofthejars/helloworld 14 | env: ["CATALINA_OPTS=-Djava.security.egd=file:/dev/./urandom"] 15 | portBindings: [8081->8080/tcp] 16 | links: 17 | - pingpong:pingpong 18 | 19 | pingpong: 20 | image: jonmorehouse/ping-pong 21 | exposedPorts: [8080/tcp] 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-api 18 | 19 | Arquillian Cube Q API 20 | 21 | 22 | 23 | junit 24 | junit 25 | test 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/QOperativeSystemChaosSimianArmy.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import org.arquillian.cube.q.api.OperativeSystemChaos; 4 | import org.arquillian.cube.spi.Cube; 5 | import org.arquillian.cube.spi.CubeRegistry; 6 | 7 | public class QOperativeSystemChaosSimianArmy implements OperativeSystemChaos { 8 | 9 | private CubeRegistry cubeRegistry; 10 | 11 | public QOperativeSystemChaosSimianArmy(CubeRegistry cubeRegistry) { 12 | this.cubeRegistry = cubeRegistry; 13 | } 14 | 15 | @Override 16 | public Action on(String containerId) { 17 | final Cube cube = cubeRegistry.getCube(containerId); 18 | 19 | if (cube == null) { 20 | throw new IllegalArgumentException( 21 | String.format("Container %s is not registered in Arquillian Cube.", containerId)); 22 | } 23 | return new QSimianArmyAction(cube); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QContainerChaosPumbaResourceProvider.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.q.api.ContainerChaos; 4 | import org.jboss.arquillian.core.api.Instance; 5 | import org.jboss.arquillian.core.api.annotation.Inject; 6 | import org.jboss.arquillian.test.api.ArquillianResource; 7 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 8 | 9 | import java.lang.annotation.Annotation; 10 | 11 | public class QContainerChaosPumbaResourceProvider implements ResourceProvider { 12 | 13 | @Inject 14 | private Instance containerChaosInst; 15 | 16 | @Override 17 | public boolean canProvide(Class type) { 18 | return type.isAssignableFrom(ContainerChaos.class); 19 | } 20 | 21 | @Override 22 | public Object lookup(ArquillianResource arquillianResource, Annotation... annotations) { 23 | return containerChaosInst.get(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ftest-toxic-reporter/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | CUBE 10 | 11 | hw: 12 | image: lordofthejars/helloworld 13 | env: ["CATALINA_OPTS=-Djava.security.egd=file:/dev/./urandom"] 14 | portBindings: [8081->8080/tcp] 15 | links: 16 | - pingpong:pingpong 17 | 18 | pingpong: 19 | image: jonmorehouse/ping-pong 20 | exposedPorts: [8080/tcp] 21 | 22 | 23 | 24 | true 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /api/src/test/java/org/arquillian/cube/q/api/QRunConditionTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import static org.hamcrest.CoreMatchers.is; 8 | import static org.junit.Assert.assertThat; 9 | 10 | public class QRunConditionTest { 11 | 12 | @Test 13 | public void shouldIterate() { 14 | final Q.IterationRunCondition iterate = Q.IterationRunCondition.times(2); 15 | 16 | assertThat(iterate.isExecutable(), is(true)); 17 | assertThat(iterate.isExecutable(), is(true)); 18 | assertThat(iterate.isExecutable(), is(false)); 19 | } 20 | 21 | @Test 22 | public void shouldApplyDuration() throws InterruptedException { 23 | final Q.DurationRunCondition during = Q.DurationRunCondition.during(5, TimeUnit.SECONDS); 24 | 25 | assertThat(during.isExecutable(), is(true)); 26 | TimeUnit.SECONDS.sleep(6); 27 | assertThat(during.isExecutable(), is(false)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/client/ToxiProxyContainer.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.client; 2 | 3 | public class ToxiProxyContainer implements ToxiProxy { 4 | 5 | private String hostIp; 6 | private int hostPort; 7 | 8 | private ToxiProxyClient client; 9 | private ToxiProxy toxiProxy; 10 | 11 | public ToxiProxyClient getClient() { 12 | if (this.client == null) { 13 | this.client = ToxiProxyClient.Builder.create(hostIp, hostPort); 14 | } 15 | return this.client; 16 | } 17 | 18 | private ToxiProxy getProxy() { 19 | if (this.toxiProxy == null) { 20 | this.toxiProxy = new ToxiProxyScenario(getClient()); 21 | } 22 | return this.toxiProxy; 23 | } 24 | 25 | public void register(String name, String listen, String upstream) { 26 | getProxy().register(name, listen, upstream); 27 | } 28 | 29 | public Scenario given(String name) { 30 | return getProxy().given(name); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/QOperativeSystemChaosSimianArmyResourceProvider.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import org.arquillian.cube.q.api.OperativeSystemChaos; 4 | import org.jboss.arquillian.core.api.Instance; 5 | import org.jboss.arquillian.core.api.annotation.Inject; 6 | import org.jboss.arquillian.test.api.ArquillianResource; 7 | import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; 8 | 9 | import java.lang.annotation.Annotation; 10 | 11 | public class QOperativeSystemChaosSimianArmyResourceProvider implements ResourceProvider { 12 | 13 | @Inject 14 | private Instance operativeSystemChaosInstance; 15 | 16 | @Override 17 | public boolean canProvide(Class type) { 18 | return type.isAssignableFrom(OperativeSystemChaos.class); 19 | } 20 | 21 | @Override 22 | public Object lookup(ArquillianResource arquillianResource, Annotation... annotations) { 23 | return operativeSystemChaosInstance.get(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ftest-toxic-frontend/src/test/resources/arquillian.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | dev 10 | CUBE 11 | 12 | hw: 13 | image: lordofthejars/helloworld 14 | env: ["CATALINA_OPTS=-Djava.security.egd=file:/dev/./urandom"] 15 | portBindings: [8081->8080/tcp] 16 | links: 17 | - pingpong:pingpong 18 | 19 | pingpong: 20 | image: jonmorehouse/ping-pong 21 | exposedPorts: [8080/tcp] 22 | 23 | 24 | 25 | true 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /spi/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-spi 18 | 19 | Arquillian Cube Q SPI 20 | 21 | 22 | 23 | org.arquillian.cube 24 | arquillian-cube-docker 25 | 26 | 27 | 28 | junit 29 | junit 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QContainerChaosPumba.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.CubeController; 4 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 5 | import org.arquillian.cube.q.api.ContainerChaos; 6 | import org.arquillian.cube.spi.CubeRegistry; 7 | 8 | public class QContainerChaosPumba implements ContainerChaos { 9 | 10 | private CubeRegistry cubeRegistry; 11 | private CubeController cubeController; 12 | private CubeDockerConfiguration cubeDockerConfiguration; 13 | 14 | public QContainerChaosPumba(CubeRegistry cubeRegistry, CubeController cubeController, 15 | CubeDockerConfiguration cubeDockerConfiguration) { 16 | this.cubeRegistry = cubeRegistry; 17 | this.cubeController = cubeController; 18 | this.cubeDockerConfiguration = cubeDockerConfiguration; 19 | } 20 | 21 | @Override 22 | public Action onCubeDockerHost() { 23 | return new QPumbaAction(this.cubeController, this.cubeRegistry, this.cubeDockerConfiguration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/QNetworkChaosToxicCreator.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.q.api.NetworkChaos; 4 | import org.arquillian.cube.q.toxic.client.ToxiProxyScenario; 5 | import org.jboss.arquillian.core.api.Injector; 6 | import org.jboss.arquillian.core.api.Instance; 7 | import org.jboss.arquillian.core.api.InstanceProducer; 8 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 9 | import org.jboss.arquillian.core.api.annotation.Inject; 10 | import org.jboss.arquillian.core.api.annotation.Observes; 11 | 12 | public class QNetworkChaosToxicCreator { 13 | 14 | @Inject 15 | @ApplicationScoped 16 | private InstanceProducer qInst; 17 | 18 | @Inject 19 | private Instance injectorInstance; 20 | 21 | public void createProxy(@Observes ToxiProxyScenario proxy) { 22 | QNetworkChaosToxic qNetworkChaosToxic = new QNetworkChaosToxic(proxy); 23 | injectorInstance.get().inject(qNetworkChaosToxic); 24 | qInst.set(qNetworkChaosToxic); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/QOperativeSystemChaosSimianArmyCreator.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import org.arquillian.cube.q.api.OperativeSystemChaos; 4 | import org.arquillian.cube.spi.CubeRegistry; 5 | import org.jboss.arquillian.core.api.Instance; 6 | import org.jboss.arquillian.core.api.InstanceProducer; 7 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 8 | import org.jboss.arquillian.core.api.annotation.Inject; 9 | import org.jboss.arquillian.core.api.annotation.Observes; 10 | import org.jboss.arquillian.test.spi.event.suite.BeforeSuite; 11 | 12 | public class QOperativeSystemChaosSimianArmyCreator { 13 | 14 | @Inject 15 | @ApplicationScoped 16 | private InstanceProducer operativeSystemChaosInstanceProducer; 17 | 18 | @Inject 19 | private Instance cubeRegistryInstance; 20 | 21 | public void createSimianArmyScripts(@Observes BeforeSuite event) { 22 | operativeSystemChaosInstanceProducer.set(new QOperativeSystemChaosSimianArmy(cubeRegistryInstance.get())); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/FillDiskSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | import org.apache.commons.lang3.text.StrSubstitutor; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class FillDiskSimianArmyChaosScript extends SimianArmyScriptChaos { 11 | 12 | private long size; 13 | 14 | public FillDiskSimianArmyChaosScript(long size) { 15 | super("filldisk"); 16 | this.size = size; 17 | } 18 | 19 | @Override 20 | public String[] postProcessScript(String[] chaosScriptsContent) { 21 | 22 | List commands = new ArrayList<>(); 23 | 24 | Map params = new HashMap<>(); 25 | params.put("size", size); 26 | StrSubstitutor substitutor = new StrSubstitutor(params); 27 | 28 | for (String chaosScriptContent : chaosScriptsContent) { 29 | commands.add(substitutor.replace(chaosScriptContent)); 30 | } 31 | 32 | return commands.toArray(new String[commands.size()]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/KillProcessSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | import org.apache.commons.lang3.text.StrSubstitutor; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class KillProcessSimianArmyChaosScript extends SimianArmyScriptChaos { 11 | 12 | private String process; 13 | 14 | public KillProcessSimianArmyChaosScript(String process) { 15 | super("killprocess"); 16 | this.process = process; 17 | } 18 | 19 | @Override 20 | public String[] postProcessScript(String[] chaosScriptsContent) { 21 | 22 | List commands = new ArrayList<>(); 23 | 24 | Map params = new HashMap<>(); 25 | params.put("process", process); 26 | 27 | StrSubstitutor substitutor = new StrSubstitutor(params); 28 | 29 | for (String chaosScriptContent : chaosScriptsContent) { 30 | commands.add(substitutor.replace(chaosScriptContent)); 31 | } 32 | 33 | return commands.toArray(new String[commands.size()]); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/BlockPortSimianArmyChaosScript.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | import org.apache.commons.lang3.text.StrSubstitutor; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class BlockPortSimianArmyChaosScript extends SimianArmyScriptChaos { 11 | 12 | private Integer[] ports; 13 | 14 | public BlockPortSimianArmyChaosScript(Integer[] ports) { 15 | super("blockport"); 16 | this.ports = ports; 17 | } 18 | 19 | @Override 20 | public String[] postProcessScript(String[] chaosScriptsContent) { 21 | 22 | final List commands = new ArrayList<>(); 23 | 24 | for (Integer port : ports) { 25 | 26 | Map params = new HashMap<>(); 27 | params.put("port", port); 28 | StrSubstitutor substitutor = new StrSubstitutor(params); 29 | 30 | for (String chaosScriptContent : chaosScriptsContent) { 31 | commands.add(substitutor.replace(chaosScriptContent)); 32 | } 33 | } 34 | 35 | return commands.toArray(new String[commands.size()]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/client/NetworkChaosConfigurator.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.client; 2 | 3 | import org.arquillian.cube.q.spi.NetworkChaosConfiguration; 4 | import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; 5 | import org.jboss.arquillian.core.api.InstanceProducer; 6 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 7 | import org.jboss.arquillian.core.api.annotation.Inject; 8 | import org.jboss.arquillian.core.api.annotation.Observes; 9 | 10 | import java.util.Map; 11 | 12 | public class NetworkChaosConfigurator { 13 | 14 | private static final String EXTENSION_NAME = "networkChaos"; 15 | 16 | @Inject 17 | @ApplicationScoped 18 | private InstanceProducer networkChaosConfigurationInstanceProducer; 19 | 20 | //Add precedence -10 because we need that ContainerRegistry is available in the Arquillian scope. 21 | public void configure(@Observes ArquillianDescriptor arquillianDescriptor) { 22 | Map config = arquillianDescriptor.extension(EXTENSION_NAME).getExtensionProperties(); 23 | NetworkChaosConfiguration cubeConfiguration = NetworkChaosConfiguration.fromMap(config); 24 | networkChaosConfigurationInstanceProducer.set(cubeConfiguration); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QContainerChaosPumbaStopper.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.CubeController; 4 | import org.arquillian.cube.q.spi.StandaloneContainer; 5 | import org.arquillian.cube.spi.Cube; 6 | import org.arquillian.cube.spi.CubeRegistry; 7 | import org.jboss.arquillian.core.api.Instance; 8 | import org.jboss.arquillian.core.api.annotation.Inject; 9 | import org.jboss.arquillian.core.api.annotation.Observes; 10 | import org.jboss.arquillian.test.spi.event.suite.AfterSuite; 11 | 12 | public class QContainerChaosPumbaStopper { 13 | 14 | @Inject 15 | private Instance cubeRegistryInstance; 16 | 17 | @Inject 18 | private Instance cubeControllerInstance; 19 | 20 | public void stopPumbaContainer(@Observes AfterSuite afterSuite) { 21 | final Cube cube = cubeRegistryInstance.get().getCube(StandaloneContainer.Builder.DEFAULT_NAME); 22 | if (cube != null) { 23 | if (cube.state() != Cube.State.STOPPED && cube.state() != Cube.State.DESTROYED) { 24 | cubeControllerInstance.get().stop(StandaloneContainer.Builder.DEFAULT_NAME); 25 | cubeControllerInstance.get().destroy(StandaloneContainer.Builder.DEFAULT_NAME); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QContainerChaosPumbaCreator.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.CubeController; 4 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 5 | import org.arquillian.cube.q.api.ContainerChaos; 6 | import org.arquillian.cube.spi.CubeRegistry; 7 | import org.jboss.arquillian.core.api.Instance; 8 | import org.jboss.arquillian.core.api.InstanceProducer; 9 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 10 | import org.jboss.arquillian.core.api.annotation.Inject; 11 | import org.jboss.arquillian.core.api.annotation.Observes; 12 | import org.jboss.arquillian.test.spi.event.suite.BeforeSuite; 13 | 14 | public class QContainerChaosPumbaCreator { 15 | 16 | @Inject 17 | @ApplicationScoped 18 | private InstanceProducer containerChaosInst; 19 | 20 | @Inject 21 | private Instance cubeRegistryInstance; 22 | 23 | @Inject 24 | private Instance cubeControllerInstance; 25 | 26 | @Inject 27 | private Instance cubeDockerConfigurationInstance; 28 | 29 | public void createPumba(@Observes BeforeSuite event) { 30 | containerChaosInst.set( 31 | new QContainerChaosPumba(cubeRegistryInstance.get(), cubeControllerInstance.get(), 32 | cubeDockerConfigurationInstance.get()) 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | ##### Issue Overview 18 | 19 | Tell us briefly what the problem is about. 20 | 21 | ##### Expected Behaviour 22 | 23 | ##### Current Behaviour 24 | 25 | ##### Steps To Reproduce 26 | 1. [step 1] 27 | 2. [step 2] 28 | 29 | ##### Additional Information 30 | 31 | Anything relevant to help us resolving the problem. For example if you are using `mvn` you can share output for `mvn --version` as well as dependencies in use `mvn dependency:tree`. OS as well as JDK version would be helpful too. 32 | 33 | For long outputs such as stacktraces please use HTML5 `
` 34 | 35 | ``` 36 |
37 | $mvn --version 38 | Maven home: /usr/share/maven/latest 39 | Java version: 1.7.0_79, vendor: Oracle Corporation 40 | Java home: /usr/java/jdk1.7.0_79/jre 41 | Default locale: en_US, platform encoding: UTF-8 42 | OS name: "linux", version: "4.7.7-200.fc24.x86_64", arch: "amd64", family: "unix" 43 |
44 | ``` 45 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/PumbaStandaloneContainerHandler.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.q.spi.StandaloneContainer; 5 | import org.arquillian.cube.q.spi.StandaloneManager; 6 | import org.jboss.arquillian.core.api.Instance; 7 | import org.jboss.arquillian.core.api.annotation.Inject; 8 | 9 | import java.util.Collections; 10 | 11 | public class PumbaStandaloneContainerHandler implements StandaloneManager { 12 | 13 | @Inject 14 | Instance cubeDockerConfigurationInstance; 15 | 16 | @Override 17 | public StandaloneContainer install() { 18 | StandaloneContainer.Builder builder = StandaloneContainer.create(); 19 | 20 | if (isNativeDocker()) { 21 | builder.volumes(Collections.singletonList("/var/run/docker.sock:/var/run/docker.sock")); 22 | } else { 23 | final CubeDockerConfiguration cubeDockerConfiguration = cubeDockerConfigurationInstance.get(); 24 | builder.volumes(Collections.singletonList(cubeDockerConfiguration.getCertPath() + ":/etc/ssl/docker")); 25 | } 26 | 27 | return builder.build(); 28 | } 29 | 30 | private boolean isNativeDocker() { 31 | final CubeDockerConfiguration cubeDockerConfiguration = cubeDockerConfigurationInstance.get(); 32 | return cubeDockerConfiguration.getDockerServerUri().startsWith("unix"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ftest-pumba/src/test/java/org/arquillian/cube/q/pumba/PumbaFunctionalTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import com.github.dockerjava.api.DockerClient; 4 | import com.github.dockerjava.api.model.Container; 5 | import org.arquillian.cube.docker.impl.requirement.RequiresDockerMachine; 6 | import org.arquillian.cube.q.api.ContainerChaos; 7 | import org.arquillian.cube.requirement.ArquillianConditionalRunner; 8 | import org.jboss.arquillian.test.api.ArquillianResource; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.List; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | @RequiresDockerMachine(name = "dev") 18 | @RunWith(ArquillianConditionalRunner.class) 19 | public class PumbaFunctionalTestCase { 20 | 21 | @ArquillianResource 22 | ContainerChaos containerChaos; 23 | 24 | @ArquillianResource 25 | DockerClient dockerClient; 26 | 27 | @Test 28 | public void shouldKillContainers() throws Exception { 29 | containerChaos 30 | .onCubeDockerHost() 31 | .killRandomly( 32 | ContainerChaos.ContainersType.regularExpression("^pingpong"), 33 | ContainerChaos.IntervalType.intervalInSeconds(4), 34 | ContainerChaos.KillSignal.SIGTERM 35 | ) 36 | .exec(); 37 | 38 | TimeUnit.SECONDS.sleep(12); 39 | 40 | final List containers = dockerClient.listContainersCmd().exec(); 41 | //Pumba container is not killed by itself 42 | assertThat(containers).hasSize(1); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pumba/src/test/java/org/arquillian/cube/q/pumba/util/PumbaCommandLineCreatorTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba.util; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.mockito.runners.MockitoJUnitRunner; 8 | 9 | import java.util.Collection; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | import static org.mockito.Mockito.when; 13 | 14 | @RunWith(MockitoJUnitRunner.class) 15 | public class PumbaCommandLineCreatorTest { 16 | 17 | @Mock 18 | CubeDockerConfiguration cubeDockerConfiguration; 19 | 20 | @Test 21 | public void shouldCreatePumbaCli() { 22 | when(cubeDockerConfiguration.getDockerServerUri()).thenReturn("https://192.168.0.1"); 23 | 24 | final Collection cli = 25 | PumbaCommandLineCreator.run("re2:^hp|10s|KILL:SIGTERM", false, cubeDockerConfiguration); 26 | assertThat(cli).containsExactly("pumba", "--host", "https://192.168.0.1", "--tlsverify", "run", "--chaos", 27 | "re2:^hp|10s|KILL:SIGTERM"); 28 | } 29 | 30 | @Test 31 | public void shouldCreatePumbaCliWithRandom() { 32 | when(cubeDockerConfiguration.getDockerServerUri()).thenReturn("https://192.168.0.1"); 33 | 34 | final Collection cli = 35 | PumbaCommandLineCreator.run("re2:^hp|10s|KILL:SIGTERM", true, cubeDockerConfiguration); 36 | assertThat(cli).containsExactly("pumba", "--host", "https://192.168.0.1", "--tlsverify", "run", "--random", 37 | "--chaos", "re2:^hp|10s|KILL:SIGTERM"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spi/src/test/java/org/arquillian/cube/q/spi/ProxyBuilderTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | import static org.hamcrest.CoreMatchers.hasItem; 4 | import static org.hamcrest.CoreMatchers.hasItems; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 8 | import org.arquillian.cube.docker.impl.client.config.ExposedPort; 9 | import org.arquillian.cube.docker.impl.client.config.PortBinding; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | 13 | public class ProxyBuilderTestCase { 14 | 15 | @Test 16 | public void shouldAddDefaultPort() { 17 | Proxy proxy = Proxy.create() 18 | .build(); 19 | 20 | CubeContainer cube = proxy.getCube(); 21 | assertThat(cube.getPortBindings(), hasItem(PortBinding.valueOf("8474/tcp"))); 22 | } 23 | 24 | @Test 25 | public void shouldExposeAdditionalPorts() { 26 | Proxy proxy = Proxy.create() 27 | .containerExpose("A", 8080, "tcp") 28 | .build(); 29 | 30 | CubeContainer cube = proxy.getCube(); 31 | assertThat(cube.getExposedPorts(), hasItem(ExposedPort.valueOf("8080/tcp"))); 32 | } 33 | 34 | @Test 35 | public void shouldBindAdditionalPorts() { 36 | Proxy proxy = Proxy.create() 37 | .containerBinds("A", 1000, 2000, "tcp") 38 | .build(); 39 | 40 | CubeContainer cube = proxy.getCube(); 41 | Assert.assertThat(cube.getPortBindings(), 42 | hasItems(PortBinding.valueOf("1000->2000/tcp"), PortBinding.valueOf("8474/tcp"))); 43 | 44 | System.out.println(proxy.getRelations()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/org/arquillian/cube/q/core/InstallProxy.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.core; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 5 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 6 | import org.arquillian.cube.docker.impl.util.ConfigUtil; 7 | import org.arquillian.cube.q.spi.Proxy; 8 | import org.arquillian.cube.q.spi.ProxyManager; 9 | import org.jboss.arquillian.core.api.Instance; 10 | import org.jboss.arquillian.core.api.InstanceProducer; 11 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 12 | import org.jboss.arquillian.core.api.annotation.Inject; 13 | import org.jboss.arquillian.core.api.annotation.Observes; 14 | import org.jboss.arquillian.core.spi.ServiceLoader; 15 | 16 | public class InstallProxy { 17 | 18 | @Inject 19 | private Instance serviceLoaderInst; 20 | 21 | @Inject @ApplicationScoped 22 | private InstanceProducer proxyInst; 23 | 24 | public void install(@Observes(precedence = 100) CubeDockerConfiguration configuration) { 25 | 26 | DockerCompositions cubes = configuration.getDockerContainersContent(); 27 | ProxyManager installer = serviceLoaderInst.get().onlyOne(ProxyManager.class); 28 | 29 | if (installer != null) { 30 | Proxy proxy = installer.install(cubes); 31 | proxyInst.set(proxy); 32 | 33 | final CubeContainer cube = proxy.getCube(); 34 | cubes.add(proxy.getName(), cube); 35 | 36 | System.out.println("PROXY INSTALLED"); 37 | System.out.println(ConfigUtil.dump(cubes)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/org/arquillian/cube/q/core/InstallStandaloneContainer.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.core; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 5 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 6 | import org.arquillian.cube.docker.impl.util.ConfigUtil; 7 | import org.arquillian.cube.q.spi.StandaloneContainer; 8 | import org.arquillian.cube.q.spi.StandaloneManager; 9 | import org.jboss.arquillian.core.api.Instance; 10 | import org.jboss.arquillian.core.api.InstanceProducer; 11 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 12 | import org.jboss.arquillian.core.api.annotation.Inject; 13 | import org.jboss.arquillian.core.api.annotation.Observes; 14 | import org.jboss.arquillian.core.spi.ServiceLoader; 15 | 16 | public class InstallStandaloneContainer { 17 | 18 | @Inject 19 | private Instance serviceLoaderInst; 20 | 21 | @Inject 22 | @ApplicationScoped 23 | private InstanceProducer standaloneContainerInst; 24 | 25 | public void install(@Observes(precedence = 100) CubeDockerConfiguration configuration) { 26 | 27 | StandaloneManager installer = serviceLoaderInst.get().onlyOne(StandaloneManager.class); 28 | 29 | if (installer != null) { 30 | DockerCompositions cubes = configuration.getDockerContainersContent(); 31 | 32 | final StandaloneContainer install = installer.install(); 33 | final CubeContainer cube = install.getCube(); 34 | cubes.add(install.getName(), cube); 35 | 36 | standaloneContainerInst.set(install); 37 | 38 | System.out.println("STANDALONE CONTAINER INSTALLED"); 39 | System.out.println(ConfigUtil.dump(cubes)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ftest-toxic-frontend/src/test/java/org/arquillian/cube/q/toxic/ToxicFunctionalTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.HostIp; 4 | import org.arquillian.cube.docker.impl.requirement.RequiresDockerMachine; 5 | import org.arquillian.cube.impl.util.IOUtil; 6 | import org.arquillian.cube.q.api.NetworkChaos; 7 | import org.arquillian.cube.requirement.ArquillianConditionalRunner; 8 | import org.jboss.arquillian.test.api.ArquillianResource; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | import java.net.URL; 14 | 15 | import static org.arquillian.cube.q.api.NetworkChaos.LatencyType.latencyInMillis; 16 | 17 | @RequiresDockerMachine(name = "dev") 18 | @RunWith(ArquillianConditionalRunner.class) 19 | public class ToxicFunctionalTestCase { 20 | 21 | @ArquillianResource 22 | private NetworkChaos networkChaos; 23 | 24 | @HostIp 25 | private String ip; 26 | 27 | @Test 28 | public void should() throws Exception { 29 | 30 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 31 | final long l = System.currentTimeMillis(); 32 | String response = IOUtil.asString(url.openStream()); 33 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 34 | Assert.assertNotNull(response); 35 | } 36 | 37 | @Test 38 | public void shouldAddLatency() throws Exception { 39 | networkChaos.on("hw", 8080).latency(latencyInMillis(4000)).exec(() -> { 40 | 41 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 42 | final long l = System.currentTimeMillis(); 43 | String response = IOUtil.asString(url.openStream()); 44 | System.out.println(response); 45 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pumba/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | arquillian-cube-q 6 | org.arquillian.cube.q 7 | 1.0.0.Final-SNAPSHOT 8 | ../pom.xml 9 | 10 | 4.0.0 11 | 12 | arquillian-cube-q-pumba 13 | Arquillian Cube Q Pumba 14 | 15 | 16 | 17 | org.arquillian.cube.q 18 | arquillian-cube-q-api 19 | 20 | 21 | org.arquillian.cube.q 22 | arquillian-cube-q-spi 23 | 24 | 25 | org.arquillian.cube.q 26 | arquillian-cube-q-core 27 | 28 | 29 | org.jboss.arquillian.core 30 | arquillian-core-impl-base 31 | 32 | 33 | org.jboss.arquillian.core 34 | arquillian-core-impl-base 35 | tests 36 | 37 | 38 | org.mockito 39 | mockito-core 40 | 41 | 42 | junit 43 | junit 44 | 45 | 46 | org.assertj 47 | assertj-core 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /simianarmy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | arquillian-cube-q 6 | org.arquillian.cube.q 7 | 1.0.0.Final-SNAPSHOT 8 | ../pom.xml 9 | 10 | 4.0.0 11 | 12 | arquillian-cube-q-simianarmy 13 | 14 | Arquillian Cube Q Simian Army 15 | 16 | 17 | 18 | org.arquillian.cube.q 19 | arquillian-cube-q-api 20 | 21 | 22 | org.arquillian.cube.q 23 | arquillian-cube-q-spi 24 | 25 | 26 | org.arquillian.cube.q 27 | arquillian-cube-q-core 28 | 29 | 30 | org.jboss.arquillian.core 31 | arquillian-core-impl-base 32 | 33 | 34 | org.jboss.arquillian.core 35 | arquillian-core-impl-base 36 | tests 37 | 38 | 39 | org.mockito 40 | mockito-core 41 | 42 | 43 | junit 44 | junit 45 | 46 | 47 | org.assertj 48 | assertj-core 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /spi/src/main/java/org/arquillian/cube/q/spi/StandaloneContainer.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | import org.arquillian.cube.docker.impl.client.config.Await; 4 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 5 | import org.arquillian.cube.docker.impl.client.config.Image; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class StandaloneContainer { 11 | 12 | private String name; 13 | private CubeContainer cube; 14 | 15 | public StandaloneContainer(String name, CubeContainer cubeContainer) { 16 | this.name = name; 17 | this.cube = cubeContainer; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public CubeContainer getCube() { 25 | return cube; 26 | } 27 | 28 | public static Builder create() { 29 | return new Builder(); 30 | } 31 | 32 | public static class Builder { 33 | 34 | public static final String DEFAULT_NAME = "pumba"; 35 | private String image = "gaiaadm/pumba:36-master"; 36 | 37 | private List volumes = new ArrayList<>(); 38 | 39 | public Builder() { 40 | } 41 | 42 | public Builder volumes(List volumes) { 43 | this.volumes.addAll(volumes); 44 | return this; 45 | } 46 | 47 | public StandaloneContainer build() { 48 | CubeContainer cube = new CubeContainer(); 49 | cube.setImage(Image.valueOf(image)); 50 | 51 | Await await = new Await(); 52 | await.setStrategy("sleeping"); 53 | await.setSleepTime("1 s"); 54 | cube.setAwait(await); 55 | 56 | cube.setManual(true); 57 | cube.setRemoveVolumes(false); 58 | 59 | cube.setBinds(volumes); 60 | 61 | return new StandaloneContainer(DEFAULT_NAME, cube); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pumba/src/test/java/org/arquillian/cube/q/pumba/PumbaChaosCommandBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.q.api.ContainerChaos; 4 | import org.arquillian.cube.q.pumba.QPumbaAction.ChaosOperation; 5 | import org.junit.Test; 6 | 7 | import static org.arquillian.cube.q.api.ContainerChaos.ContainersType.containers; 8 | import static org.arquillian.cube.q.api.ContainerChaos.ContainersType.regularExpression; 9 | import static org.arquillian.cube.q.api.ContainerChaos.IntervalType.intervalInSeconds; 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | public class PumbaChaosCommandBuilderTest { 13 | 14 | @Test 15 | public void shouldCreateStopCommand() { 16 | final String stopCommand = QPumbaAction.PumbaChaosCommandBuilder.create() 17 | .containers(containers("a", "b")) 18 | .interval(intervalInSeconds(4)) 19 | .chaosOperation(ChaosOperation.STOP) 20 | .build(); 21 | 22 | assertThat(stopCommand).isEqualTo("a,b|4s|STOP"); 23 | } 24 | 25 | @Test 26 | public void shouldCreateRemoveCommand() { 27 | final String removeCommand = QPumbaAction.PumbaChaosCommandBuilder.create() 28 | .containers(regularExpression("^pingpong")) 29 | .interval(intervalInSeconds(4)) 30 | .chaosOperation(ChaosOperation.RM) 31 | .build(); 32 | 33 | assertThat(removeCommand).isEqualTo("re2:^pingpong|4s|RM"); 34 | } 35 | 36 | @Test 37 | public void shouldCreateKillCommand() { 38 | final String killCommand = QPumbaAction.PumbaChaosCommandBuilder.create() 39 | .containers(containers("a")) 40 | .interval(intervalInSeconds(4)) 41 | .chaosOperation(ChaosOperation.KILL) 42 | .killSignal(ContainerChaos.KillSignal.SIGTERM) 43 | .build(); 44 | 45 | assertThat(killCommand).isEqualTo("a|4s|KILL:SIGTERM"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pumba/src/test/java/org/arquillian/cube/q/pumba/PumbaStandaloneContainerHandlerTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.q.spi.StandaloneContainer; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | import static org.mockito.Mockito.when; 12 | 13 | @RunWith(MockitoJUnitRunner.class) 14 | public class PumbaStandaloneContainerHandlerTest { 15 | 16 | @Mock 17 | CubeDockerConfiguration cubeDockerConfiguration; 18 | 19 | @Test 20 | public void shouldAddUnixVolume() { 21 | 22 | when(cubeDockerConfiguration.getDockerServerUri()).thenReturn("unix:///var"); 23 | PumbaStandaloneContainerHandler pumbaStandaloneContainerHandler = new PumbaStandaloneContainerHandler(); 24 | pumbaStandaloneContainerHandler.cubeDockerConfigurationInstance = () -> cubeDockerConfiguration; 25 | 26 | final StandaloneContainer install = pumbaStandaloneContainerHandler.install(); 27 | assertThat(install.getCube().getBinds()).contains("/var/run/docker.sock:/var/run/docker.sock"); 28 | } 29 | 30 | @Test 31 | public void shouldAddCertsVolume() { 32 | 33 | when(cubeDockerConfiguration.getDockerServerUri()).thenReturn("https://192.168.0.1"); 34 | when(cubeDockerConfiguration.getCertPath()).thenReturn("/home/user/.machine/ssl"); 35 | PumbaStandaloneContainerHandler pumbaStandaloneContainerHandler = new PumbaStandaloneContainerHandler(); 36 | pumbaStandaloneContainerHandler.cubeDockerConfigurationInstance = () -> cubeDockerConfiguration; 37 | 38 | final StandaloneContainer install = pumbaStandaloneContainerHandler.install(); 39 | assertThat(install.getCube().getBinds()).contains("/home/user/.machine/ssl:/etc/ssl/docker"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ftest-toxic-reporter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | arquillian-cube-q 7 | org.arquillian.cube.q 8 | 1.0.0.Final-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | arquillian-cube-q-reporter-ftest-toxic 14 | 15 | Arquillian Cube Q Toxic Reporter Functional Test 16 | 17 | 18 | 19 | 20 | org.jboss.arquillian 21 | arquillian-bom 22 | ${version.arquillian_core} 23 | pom 24 | import 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.jboss.arquillian.junit 32 | arquillian-junit-standalone 33 | test 34 | 35 | 36 | junit 37 | junit 38 | test 39 | 40 | 41 | org.arquillian.cube.q 42 | arquillian-cube-q-reporter 43 | test 44 | 45 | 46 | org.arquillian.reporter 47 | arquillian-reporter-depchain 48 | pom 49 | 50 | 51 | org.assertj 52 | assertj-core 53 | test 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/client/SimianArmyScriptChaos.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy.client; 2 | 3 | import org.arquillian.cube.impl.util.IOUtil; 4 | import org.arquillian.cube.spi.Cube; 5 | import org.arquillian.cube.spi.metadata.CanExecuteProcessInContainer; 6 | 7 | /** 8 | * Base class to execute scripts to given cube. 9 | */ 10 | public abstract class SimianArmyScriptChaos { 11 | 12 | private static final String SCRIPTS_PACKAGE = "scripts"; 13 | 14 | private String chaosScript; 15 | private String[] chaosScriptContent; 16 | 17 | public SimianArmyScriptChaos(String chaosScript) { 18 | this.chaosScript = chaosScript; 19 | this.chaosScriptContent = IOUtil.asArrayString( 20 | SimianArmyScriptChaos.class.getResourceAsStream("/" + SCRIPTS_PACKAGE + "/" + this.chaosScript)); 21 | } 22 | 23 | /** 24 | * Method that in case of scripts that requires to inject some values to the ones provided by the caller must 25 | * implement to substitute the values on given script. 26 | * Also can be used to modify the script in any situation it might require external parameters 27 | * 28 | * @param chaosScriptContent 29 | * of script as read. 30 | * 31 | * @return Chaos script with the substitutions. 32 | */ 33 | public String[] postProcessScript(String[] chaosScriptContent) { 34 | return chaosScriptContent; 35 | } 36 | 37 | public void apply(Cube cube) { 38 | String[] scriptsToExecute = postProcessScript(this.chaosScriptContent); 39 | 40 | for (String scriptToExecute : scriptsToExecute) { 41 | if (cube.hasMetadata(CanExecuteProcessInContainer.class)) { 42 | final String[] command = scriptToExecute.split("\\s+"); 43 | final CanExecuteProcessInContainer executeProcess = 44 | (CanExecuteProcessInContainer) cube.getMetadata(CanExecuteProcessInContainer.class); 45 | executeProcess.exec(command); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ftest-toxic-frontend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-ftest-toxic-frontend 18 | 19 | Arquillian Cube Q Toxic FrontEnd Functional Test 20 | 21 | 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | 29 | org.jboss.arquillian 30 | arquillian-bom 31 | ${version.arquillian_core} 32 | pom 33 | import 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.arquillian.cube.q 41 | arquillian-cube-q-toxic 42 | test 43 | 44 | 45 | org.jboss.arquillian.junit 46 | arquillian-junit-standalone 47 | test 48 | 49 | 50 | junit 51 | junit 52 | 53 | 54 | org.arquillian.cube 55 | arquillian-cube-requirement 56 | test 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /ftest-toxic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-ftest-toxic 18 | 19 | Arquillian Cube Q Toxic Functional Test 20 | 21 | 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | 29 | org.jboss.arquillian 30 | arquillian-bom 31 | ${version.arquillian_core} 32 | pom 33 | import 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.arquillian.cube.q 41 | arquillian-cube-q-toxic 42 | test 43 | 44 | 45 | org.jboss.arquillian.junit 46 | arquillian-junit-standalone 47 | test 48 | 49 | 50 | junit 51 | junit 52 | test 53 | 54 | 55 | org.arquillian.cube 56 | arquillian-cube-requirement 57 | test 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /ftest-simianarmy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | arquillian-cube-q 6 | org.arquillian.cube.q 7 | 1.0.0.Final-SNAPSHOT 8 | ../pom.xml 9 | 10 | 4.0.0 11 | 12 | arquillia-cube-q-ftest-simianarmy 13 | 14 | Arquillian Cube Q Simian Army Functional Test 15 | 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | org.jboss.arquillian 25 | arquillian-bom 26 | ${version.arquillian_core} 27 | pom 28 | import 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.arquillian.cube.q 36 | arquillian-cube-q-simianarmy 37 | test 38 | 39 | 40 | org.jboss.arquillian.junit 41 | arquillian-junit-standalone 42 | test 43 | 44 | 45 | junit 46 | junit 47 | 48 | 49 | org.assertj 50 | assertj-core 51 | 52 | 53 | org.arquillian.cube 54 | arquillian-cube-requirement 55 | test 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /reporter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | arquillian-cube-q 7 | org.arquillian.cube.q 8 | 1.0.0.Final-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | arquillian-cube-q-reporter 14 | 15 | 16 | 17 | org.arquillian.reporter 18 | arquillian-core-reporter-api 19 | provided 20 | 21 | 22 | org.arquillian.reporter 23 | arquillian-reporter-impl 24 | provided 25 | 26 | 27 | org.arquillian.cube.q 28 | arquillian-cube-q-spi 29 | 30 | 31 | org.arquillian.cube.q 32 | arquillian-cube-q-toxic 33 | 34 | 35 | junit 36 | junit 37 | test 38 | 39 | 40 | org.mockito 41 | mockito-core 42 | test 43 | 44 | 45 | org.assertj 46 | assertj-core 47 | test 48 | 49 | 50 | com.jayway.jsonpath 51 | json-path-assert 52 | 53 | 54 | org.arquillian.reporter 55 | arquillian-reporter-impl 56 | tests 57 | test 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/util/PumbaCommandLineCreator.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba.util; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | 8 | public class PumbaCommandLineCreator { 9 | 10 | private final static int PUMBA_INDEX = 0; 11 | private final static int HOST_INDEX = 1; 12 | private final static int IP_INDEX = 2; 13 | private final static int TLS_INDEX = 3; 14 | private final static int RUN_INDEX = 4; 15 | private final static int RANDOM_INDEX = 5; 16 | private final static int CHAOS_INDEX = 6; 17 | private final static int CHAOS_COMMAND_INDEX = 7; 18 | 19 | private PumbaCommandLineCreator() { 20 | super(); 21 | } 22 | 23 | public static Collection run(String chaosCommand, boolean random, 24 | CubeDockerConfiguration cubeDockerConfiguration) { 25 | 26 | String[] runningCommand = new String[8]; 27 | runningCommand[PUMBA_INDEX] = "pumba"; 28 | runningCommand[RUN_INDEX] = "run"; 29 | runningCommand[CHAOS_INDEX] = "--chaos"; 30 | 31 | if (random) { 32 | runningCommand[RANDOM_INDEX] = "--random"; 33 | } 34 | 35 | if (!isNativeDocker(cubeDockerConfiguration)) { 36 | runningCommand[HOST_INDEX] = "--host"; 37 | runningCommand[IP_INDEX] = cubeDockerConfiguration.getDockerServerUri(); 38 | runningCommand[TLS_INDEX] = "--tlsverify"; 39 | } 40 | 41 | runningCommand[CHAOS_COMMAND_INDEX] = chaosCommand; 42 | 43 | return createCommandLine(runningCommand); 44 | } 45 | 46 | private static boolean isNativeDocker(CubeDockerConfiguration cubeDockerConfiguration) { 47 | return cubeDockerConfiguration.getDockerServerUri().startsWith("unix"); 48 | } 49 | 50 | private static Collection createCommandLine(String[] cmd) { 51 | Collection cli = new ArrayList<>(); 52 | for (String c : cmd) { 53 | if (c != null && !"".equals(c.trim())) { 54 | cli.add(c); 55 | } 56 | } 57 | 58 | return cli; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/ContainerChaos.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public interface ContainerChaos { 6 | 7 | Action onCubeDockerHost(); 8 | 9 | interface Action extends Q { 10 | Action stop(ContainersType containersType, IntervalType intervalType); 11 | 12 | Action stopRandomly(ContainersType containersType, IntervalType intervalType); 13 | 14 | Action remove(ContainersType containersType, IntervalType intervalType); 15 | 16 | Action removeRandomly(ContainersType containersType, IntervalType intervalType); 17 | 18 | Action kill(ContainersType containersType, IntervalType intervalType, KillSignal killSignal); 19 | 20 | Action killRandomly(ContainersType containersType, IntervalType intervalType, KillSignal killSignal); 21 | } 22 | 23 | enum KillSignal 24 | 25 | { 26 | SIGHUP, SIGINT, SIGKILL, SIGTERM, SIGSTOP 27 | } 28 | 29 | final class ContainersType extends Q.StringType { 30 | 31 | protected ContainersType(String value) { 32 | super(value); 33 | } 34 | 35 | public static ContainersType regularExpression(String expression) { 36 | return new ContainersType("re2:" + expression); 37 | } 38 | 39 | public static ContainersType containers(String... containers) { 40 | return new ContainersType(joiner(containers)); 41 | } 42 | 43 | private static String joiner(String... containers) { 44 | StringBuilder csv = new StringBuilder(); 45 | for (String container : containers) { 46 | csv.append(container).append(","); 47 | } 48 | 49 | return csv.substring(0, csv.length() - 1); 50 | } 51 | } 52 | 53 | final class IntervalType extends Q.LongType { 54 | 55 | protected IntervalType(long value) { 56 | super(value); 57 | } 58 | 59 | public static IntervalType intervalInSeconds(long interval) { 60 | return new IntervalType(interval); 61 | } 62 | 63 | public static IntervalType interval(long interval, TimeUnit timeUnit) { 64 | return new IntervalType(timeUnit.toSeconds(interval)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ftest-simianarmy/src/test/java/org/arquillian/cube/q/simianarmy/SimianArmyFunctionalTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import com.github.dockerjava.api.DockerClient; 4 | import org.arquillian.cube.HostIp; 5 | import org.arquillian.cube.HostPort; 6 | import org.arquillian.cube.docker.impl.requirement.RequiresDockerMachine; 7 | import org.arquillian.cube.q.api.OperativeSystemChaos; 8 | import org.arquillian.cube.requirement.ArquillianConditionalRunner; 9 | import org.jboss.arquillian.test.api.ArquillianResource; 10 | import org.junit.Ignore; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.InputStreamReader; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | 19 | import static org.arquillian.cube.q.api.OperativeSystemChaos.NumberCpuType.singleCpu; 20 | 21 | @RequiresDockerMachine(name = "dev") 22 | @RunWith(ArquillianConditionalRunner.class) 23 | public class SimianArmyFunctionalTestCase { 24 | 25 | @ArquillianResource 26 | OperativeSystemChaos operativeSystemChaos; 27 | 28 | @ArquillianResource 29 | DockerClient dockerClient; 30 | 31 | @HostIp 32 | String dockerHost; 33 | 34 | @HostPort(containerName = "pingpong ", value = 8080) 35 | int port; 36 | 37 | @Test(expected = Exception.class) @Ignore //Running this test in same machine makes everything screwed 38 | public void shouldExecuteBurnCpuChaos() throws Exception { 39 | operativeSystemChaos.on("pingpong").burnCpu(singleCpu()).exec(); 40 | 41 | URL obj = new URL("http://" + dockerHost + ":" + port); 42 | HttpURLConnection con = (HttpURLConnection) obj.openConnection(); 43 | 44 | // optional default is GET 45 | con.setRequestMethod("GET"); 46 | 47 | //add request header 48 | con.setRequestProperty("User-Agent", "Http URL"); 49 | 50 | int responseCode = con.getResponseCode(); 51 | BufferedReader in = new BufferedReader( 52 | new InputStreamReader(con.getInputStream())); 53 | String inputLine; 54 | StringBuilder response = new StringBuilder(); 55 | 56 | while ((inputLine = in.readLine()) != null) { 57 | response.append(inputLine); 58 | } 59 | in.close(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /toxic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-toxic 18 | 19 | Arquillian Cube Q Toxic 20 | 21 | 22 | 23 | org.arquillian.cube.q 24 | arquillian-cube-q-api 25 | 26 | 27 | org.arquillian.cube.q 28 | arquillian-cube-q-spi 29 | 30 | 31 | org.arquillian.cube.q 32 | arquillian-cube-q-core 33 | 34 | 35 | eu.rekawek.toxiproxy 36 | toxiproxy-java 37 | 38 | 39 | 40 | org.jboss.arquillian.core 41 | arquillian-core-impl-base 42 | 43 | 44 | 45 | org.jboss.arquillian.core 46 | arquillian-core-impl-base 47 | tests 48 | 49 | 50 | org.jboss.arquillian.config 51 | arquillian-config-api 52 | 53 | 54 | org.mockito 55 | mockito-core 56 | 57 | 58 | junit 59 | junit 60 | 61 | 62 | org.assertj 63 | assertj-core 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | addons: 5 | apt: 6 | sources: 7 | - git-core 8 | packages: 9 | - git 10 | 11 | language: java 12 | jdk: 13 | - oraclejdk8 14 | cache: 15 | directories: 16 | - $HOME/.m2 17 | 18 | before_install: 19 | - sed -i.bak -e 's|http://repo.maven.apache.org/maven2|https://repo.maven.apache.org/maven2|g' $HOME/.m2/settings.xml 20 | - BRANCH=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} 21 | - '[ $(git log --format=%B $TRAVIS_COMMIT_RANGE | grep -i "#doc" | wc -l) -gt 0 ] && FORCE_DOC_GEN=0 || FORCE_DOC_GEN=1' 22 | - MODIFIED_DOCS=$(git diff --name-only $TRAVIS_COMMIT_RANGE | grep -E 'README.adoc|^docs/.*.adoc$' | wc -l) 23 | - '[ $BRANCH == "master" ] && [ $MODIFIED_DOCS -ge 1 ] && GENERATE_DOC=0 || GENERATE_DOC=1' 24 | - 'if [ $FORCE_DOC_GEN == 0 ] || [ $GENERATE_DOC == 0 ]; then 25 | git config user.name "${GH_USER}"; 26 | git config user.email "${GH_EMAIL}"; 27 | git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*; 28 | git fetch --unshallow origin gh-pages || git fetch origin gh-pages; 29 | git worktree add -b gh-pages gh-pages origin/gh-pages; 30 | GH_REF=$(git remote get-url origin | awk "{sub(/https:\/\//,\"https://${GH_TOKEN}@\")}; 1" | awk "{sub(/\.git/, \"\")} 1"); 31 | docker pull rochdev/alpine-asciidoctor:mini; 32 | fi' 33 | 34 | before_script: ./mvnw install -q -U -DskipTests=true 35 | 36 | script: 37 | - ./mvnw clean install 38 | - 'if [ $FORCE_DOC_GEN == 0 ] || [ $GENERATE_DOC == 0 ]; then 39 | docker run -v $TRAVIS_BUILD_DIR:/docs/ --name adoc-to-html rochdev/alpine-asciidoctor:mini asciidoctor /docs/README.adoc -a generated-doc=true -a asciidoctor-source=/docs/docs -o /docs/gh-pages/index.html; 40 | fi' 41 | 42 | after_success: 43 | - 'if [ $FORCE_DOC_GEN == 0 ] || [ $GENERATE_DOC == 0 ]; then 44 | cd gh-pages; 45 | git add .; 46 | git commit -m"Publishes new documentation"; 47 | git push --quiet "${GH_REF}" gh-pages > /dev/null 2>&1; 48 | fi' 49 | 50 | after_error: 51 | - 'if [ $FORCE_DOC_GEN == 0 ] || [ $GENERATE_DOC == 0 ]; then 52 | docker logs adoc-to-html; 53 | fi' 54 | 55 | after_failure: 56 | - 'if [ $FORCE_DOC_GEN == 0 ] || [ $GENERATE_DOC == 0 ]; then 57 | docker logs adoc-to-html; 58 | fi' 59 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.arquillian.cube.q 8 | arquillian-cube-q 9 | 1.0.0.Final-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | arquillian-cube-q-core 18 | 19 | Arquillian Cube Q Core 20 | 21 | 22 | 23 | org.arquillian.cube.q 24 | arquillian-cube-q-api 25 | 26 | 27 | org.arquillian.cube.q 28 | arquillian-cube-q-spi 29 | 30 | 31 | 32 | org.arquillian.cube 33 | arquillian-cube-core 34 | 35 | 36 | org.arquillian.cube 37 | arquillian-cube-docker 38 | 39 | 40 | 41 | org.jboss.arquillian.config 42 | arquillian-config-impl-base 43 | ${version.arquillian_core} 44 | test 45 | 46 | 47 | org.jboss.arquillian.core 48 | arquillian-core-impl-base 49 | test 50 | 51 | 52 | org.jboss.arquillian.core 53 | arquillian-core-impl-base 54 | tests 55 | test 56 | 57 | 58 | junit 59 | junit 60 | test 61 | 62 | 63 | org.mockito 64 | mockito-core 65 | test 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /ftest-pumba/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | arquillian-cube-q 6 | org.arquillian.cube.q 7 | 1.0.0.Final-SNAPSHOT 8 | ../pom.xml 9 | 10 | 4.0.0 11 | 12 | arquillian-cube-q-ftest-pumba 13 | 14 | Arquillian Cube Q Pumba Functional Test 15 | 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | 24 | org.jboss.arquillian 25 | arquillian-bom 26 | ${version.arquillian_core} 27 | pom 28 | import 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.arquillian.cube.q 36 | arquillian-cube-q-pumba 37 | test 38 | 39 | 40 | org.jboss.arquillian.junit 41 | arquillian-junit-standalone 42 | test 43 | 44 | 45 | junit 46 | junit 47 | 48 | 49 | org.assertj 50 | assertj-core 51 | 52 | 53 | io.rest-assured 54 | rest-assured 55 | 3.3.0 56 | test 57 | 58 | 59 | io.rest-assured 60 | json-path 61 | 3.3.0 62 | test 63 | 64 | 65 | org.arquillian.cube 66 | arquillian-cube-requirement 67 | test 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /core/src/main/java/org/arquillian/cube/q/core/RegisterProxy.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.core; 2 | 3 | import org.arquillian.cube.q.spi.Proxy; 4 | import org.arquillian.cube.q.spi.ProxyManager; 5 | import org.arquillian.cube.spi.Cube; 6 | import org.arquillian.cube.spi.CubeRegistry; 7 | import org.arquillian.cube.spi.event.lifecycle.AfterAutoStart; 8 | import org.arquillian.cube.spi.event.lifecycle.AfterStop; 9 | import org.jboss.arquillian.core.api.Instance; 10 | import org.jboss.arquillian.core.api.annotation.Inject; 11 | import org.jboss.arquillian.core.api.annotation.Observes; 12 | import org.jboss.arquillian.core.spi.ServiceLoader; 13 | 14 | public class RegisterProxy { 15 | 16 | @Inject 17 | private Instance proxyInst; 18 | 19 | @Inject 20 | private Instance serviceLoaderInst; 21 | 22 | public void registerToxiProxyProxies(@Observes AfterAutoStart event, CubeRegistry registry) { 23 | Proxy proxy = proxyInst.get(); 24 | if (proxy != null) { 25 | Cube cube = registry.getCube(proxy.getName()); 26 | 27 | final ProxyManager proxyManager = serviceLoaderInst.get().onlyOne(ProxyManager.class); 28 | if (cube != null) { 29 | proxyManager.proxyStarted(cube); 30 | } 31 | 32 | proxyManager.populateProxies(); 33 | } 34 | } 35 | 36 | /** 37 | * public void registerProxy(AfterStart event, CubeRegistry registry) { 38 | *

39 | * Proxy proxy = proxyInst.get(); 40 | * Cube cube = registry.getCube(event.getCubeId()); 41 | * if(cube != null && isNotProxyCube(cube, proxy)) { 42 | * serviceLoaderInst.get().onlyOne(ProxyManager.class).cubeStarted(cube); 43 | * } 44 | * } 45 | *

46 | * public void createProxyClient(AfterStart event, CubeRegistry registry) { 47 | *

48 | * Proxy proxy = proxyInst.get(); 49 | *

50 | * Cube cube = registry.getCube(event.getCubeId()); 51 | * if(cube != null && isProxyCube(cube, proxy)) { 52 | * serviceLoaderInst.get().onlyOne(ProxyManager.class).proxyStarted(cube); 53 | * } 54 | *

55 | * } 56 | **/ 57 | 58 | public void unregisterProxy(@Observes AfterStop event, CubeRegistry registry) { 59 | Proxy proxy = proxyInst.get(); 60 | if (proxy != null) { 61 | Cube cube = registry.getCube(event.getCubeId()); 62 | if (cube != null && isNotProxyCube(cube, proxy)) { 63 | serviceLoaderInst.get().onlyOne(ProxyManager.class).cubeStopped(cube); 64 | } 65 | } 66 | } 67 | 68 | private boolean isNotProxyCube(Cube cube, Proxy proxy) { 69 | return !isProxyCube(cube, proxy); 70 | } 71 | 72 | private boolean isProxyCube(Cube cube, Proxy proxy) { 73 | return proxy.getName().equals(cube.getId()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/test/java/org/arquillian/cube/q/InstallProxyTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 5 | import org.arquillian.cube.q.core.InstallProxy; 6 | import org.arquillian.cube.q.spi.Proxy; 7 | import org.arquillian.cube.q.spi.ProxyManager; 8 | import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor; 9 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 10 | import org.jboss.arquillian.core.spi.ServiceLoader; 11 | import org.jboss.arquillian.core.test.AbstractManagerTestBase; 12 | import org.junit.Assert; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.mockito.Mock; 17 | import org.mockito.Mockito; 18 | import org.mockito.runners.MockitoJUnitRunner; 19 | 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | @RunWith(MockitoJUnitRunner.class) 25 | public class InstallProxyTestCase extends AbstractManagerTestBase { 26 | 27 | private static final String CONTENT = 28 | "a:\n" + 29 | " image: a/a\n" + 30 | " portBindings: [8089/tcp]\n" + 31 | " links:\n" + 32 | " - b:b\n" + 33 | "b:\n" + 34 | " image: b/b\n" + 35 | " exposedPorts: [2112/tcp]\n"; 36 | 37 | @Mock 38 | private ArquillianDescriptor descriptor; 39 | 40 | @Mock 41 | private ServiceLoader serviceLoader; 42 | 43 | @Mock 44 | private ProxyManager proxyManager; 45 | 46 | @Override 47 | protected void addExtensions(List> extensions) { 48 | extensions.add(InstallProxy.class); 49 | } 50 | 51 | @Before 52 | public void setup() { 53 | 54 | Proxy p = new Proxy.Builder().build(); 55 | 56 | bind(ApplicationScoped.class, ArquillianDescriptor.class, descriptor); 57 | bind(ApplicationScoped.class, ServiceLoader.class, serviceLoader); 58 | 59 | Mockito.when(serviceLoader.onlyOne(ProxyManager.class)).thenReturn(proxyManager); 60 | Mockito.when(proxyManager.install(Mockito.any(DockerCompositions.class))).thenReturn(p); 61 | } 62 | 63 | @Test 64 | public void shouldInstallProxy() throws Exception { 65 | CubeDockerConfiguration config = createConfig(CONTENT); 66 | fire(config); 67 | 68 | DockerCompositions cubes = config.getDockerContainersContent(); 69 | Assert.assertEquals(3, cubes.getContainerIds().size()); 70 | 71 | System.out.println(config.toString()); 72 | } 73 | 74 | private CubeDockerConfiguration createConfig(String content) { 75 | Map parameters = new HashMap<>(); 76 | 77 | parameters.put("serverVersion", "1.13"); 78 | parameters.put("serverUri", "http://localhost:25123"); 79 | parameters.put("dockerContainers", content); 80 | 81 | return CubeDockerConfiguration.fromMap(parameters, null); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /simianarmy/src/main/java/org/arquillian/cube/q/simianarmy/QSimianArmyAction.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.simianarmy; 2 | 3 | import org.arquillian.cube.q.api.OperativeSystemChaos; 4 | import org.arquillian.cube.q.simianarmy.client.BlockPortSimianArmyChaosScript; 5 | import org.arquillian.cube.q.simianarmy.client.BurnCpuSimianArmyChaosScript; 6 | import org.arquillian.cube.q.simianarmy.client.BurnIoSimianArmyChaosScript; 7 | import org.arquillian.cube.q.simianarmy.client.FillDiskSimianArmyChaosScript; 8 | import org.arquillian.cube.q.simianarmy.client.KillProcessSimianArmyChaosScript; 9 | import org.arquillian.cube.q.simianarmy.client.NullRouteSimianArmyChaosScript; 10 | import org.arquillian.cube.q.simianarmy.client.SimianArmyScriptChaos; 11 | import org.arquillian.cube.spi.Cube; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class QSimianArmyAction implements OperativeSystemChaos.Action { 17 | 18 | private Cube cube; 19 | private List scripts = new ArrayList<>(); 20 | 21 | public QSimianArmyAction(Cube cube) { 22 | this.cube = cube; 23 | } 24 | 25 | @Override 26 | public OperativeSystemChaos.Action burnCpu(OperativeSystemChaos.NumberCpuType numberCpu) { 27 | scripts.add(new BurnCpuSimianArmyChaosScript(numberCpu.getValue())); 28 | return this; 29 | } 30 | 31 | @Override 32 | public OperativeSystemChaos.Action burnIo() { 33 | scripts.add(new BurnIoSimianArmyChaosScript()); 34 | return this; 35 | } 36 | 37 | @Override 38 | public OperativeSystemChaos.Action failDns() { 39 | return blockPort(OperativeSystemChaos.PortSizeType.port(53)); 40 | } 41 | 42 | @Override 43 | public OperativeSystemChaos.Action fillDisk(OperativeSystemChaos.SizeType size) { 44 | scripts.add(new FillDiskSimianArmyChaosScript(size.getValue())); 45 | return this; 46 | } 47 | 48 | @Override 49 | public OperativeSystemChaos.Action killProcess(String processName) { 50 | scripts.add(new KillProcessSimianArmyChaosScript(processName)); 51 | return this; 52 | } 53 | 54 | @Override 55 | public OperativeSystemChaos.Action nullRoute() { 56 | scripts.add(new NullRouteSimianArmyChaosScript()); 57 | return this; 58 | } 59 | 60 | @Override 61 | public OperativeSystemChaos.Action blockPort(OperativeSystemChaos.PortSizeType port) { 62 | scripts.add(new BlockPortSimianArmyChaosScript(port.getValue())); 63 | return this; 64 | } 65 | 66 | @Override 67 | public void exec() throws Exception { 68 | executeScripts(); 69 | } 70 | 71 | @Override 72 | public void exec(Perform perform) throws Exception { 73 | try { 74 | executeScripts(); 75 | perform.execute(); 76 | } finally { 77 | killDDProcess(); 78 | } 79 | } 80 | 81 | @Override 82 | public void exec(RunCondition runCondition, Perform perform) throws Exception { 83 | try { 84 | executeScripts(); 85 | while (runCondition.isExecutable()) { 86 | perform.execute(); 87 | } 88 | } finally { 89 | killDDProcess(); 90 | } 91 | } 92 | 93 | private void killDDProcess() { 94 | new KillProcessSimianArmyChaosScript("dd").apply(cube); 95 | } 96 | 97 | private void executeScripts() { 98 | for (SimianArmyScriptChaos script : this.scripts) { 99 | script.apply(this.cube); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/OperativeSystemChaos.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | /** 4 | * Interface for executing chaos at operative system level. 5 | */ 6 | public interface OperativeSystemChaos { 7 | 8 | /** 9 | * Method to set in which container you want to run the process. 10 | * 11 | * @param containerId 12 | * of container. 13 | * 14 | * @return Action element to set the chaos. 15 | */ 16 | Action on(String containerId); 17 | 18 | /** 19 | * Interface that abstracts on how operative system chaos is implemented 20 | */ 21 | interface Action extends Q { 22 | 23 | /** 24 | * Burn CPU setting CPU to 100% 25 | * 26 | * @param numberCpu 27 | * to run this process. 28 | * 29 | * @return this element. 30 | */ 31 | Action burnCpu(NumberCpuType numberCpu); 32 | 33 | /** 34 | * Burn IO channels 35 | * 36 | * @return this element. 37 | */ 38 | Action burnIo(); 39 | 40 | /** 41 | * Provoke a fail in DNS server (blocking port 53) 42 | * 43 | * @return this element. 44 | */ 45 | Action failDns(); 46 | 47 | /** 48 | * Fill the disk with trash 49 | * 50 | * @param size 51 | * of the trash 52 | * 53 | * @return this element. 54 | */ 55 | Action fillDisk(SizeType size); 56 | 57 | /** 58 | * Kills process in specified interval 59 | * 60 | * @param processName 61 | * to kill 62 | * 63 | * @return this element. 64 | */ 65 | Action killProcess(String processName); 66 | 67 | /** 68 | * Set null route 69 | * 70 | * @return this element. 71 | */ 72 | Action nullRoute(); 73 | 74 | /** 75 | * Blocks a port 76 | * 77 | * @param port 78 | * port(s) to block 79 | * 80 | * @return this element. 81 | */ 82 | Action blockPort(PortSizeType port); 83 | } 84 | 85 | final class NumberCpuType extends Q.IntegerType { 86 | 87 | protected NumberCpuType(int value) { 88 | super(value); 89 | } 90 | 91 | public static NumberCpuType cpus(int cpu) { 92 | return new NumberCpuType(cpu); 93 | } 94 | 95 | public static NumberCpuType singleCpu() { 96 | return new NumberCpuType(1); 97 | } 98 | } 99 | 100 | final class SizeType extends Q.LongType { 101 | 102 | protected SizeType(long value) { 103 | super(value); 104 | } 105 | 106 | /** 107 | * Default size of 65GB 108 | * 109 | * @return Size element. 110 | */ 111 | public static SizeType defaultSize() { 112 | return new SizeType(65536); 113 | } 114 | 115 | public static SizeType sizeInMegas(long size) { 116 | return new SizeType(size); 117 | } 118 | } 119 | 120 | final class PortSizeType extends Q.ArrayType { 121 | 122 | protected PortSizeType(Integer[] value) { 123 | super(value); 124 | } 125 | 126 | public static PortSizeType port(Integer port) { 127 | return new PortSizeType(new Integer[] {port}); 128 | } 129 | 130 | public static PortSizeType ports(Integer... ports) { 131 | return new PortSizeType(ports); 132 | } 133 | 134 | public static PortSizeType portRange(int start, int stop) { 135 | int totalNumber = (stop - start) + 1; 136 | Integer[] ports = new Integer[totalNumber]; 137 | 138 | int currentPort = start; 139 | for (int i = 0; i < ports.length; i++) { 140 | ports[i] = currentPort; 141 | currentPort++; 142 | } 143 | 144 | return new PortSizeType(ports); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /simianarmy/src/test/java/org/arquillia/cube/q/simianarmy/client/SimianArmyScriptChaosTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillia.cube.q.simianarmy.client; 2 | 3 | import org.arquillian.cube.q.simianarmy.client.BlockPortSimianArmyChaosScript; 4 | import org.arquillian.cube.q.simianarmy.client.BurnCpuSimianArmyChaosScript; 5 | import org.arquillian.cube.q.simianarmy.client.BurnIoSimianArmyChaosScript; 6 | import org.arquillian.cube.q.simianarmy.client.FillDiskSimianArmyChaosScript; 7 | import org.arquillian.cube.q.simianarmy.client.KillProcessSimianArmyChaosScript; 8 | import org.arquillian.cube.q.simianarmy.client.NullRouteSimianArmyChaosScript; 9 | import org.arquillian.cube.spi.Cube; 10 | import org.arquillian.cube.spi.metadata.CanExecuteProcessInContainer; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.mockito.Mock; 15 | import org.mockito.runners.MockitoJUnitRunner; 16 | 17 | import static org.mockito.Mockito.times; 18 | import static org.mockito.Mockito.verify; 19 | import static org.mockito.Mockito.when; 20 | 21 | @RunWith(MockitoJUnitRunner.class) 22 | public class SimianArmyScriptChaosTest { 23 | 24 | @Mock 25 | Cube cube; 26 | 27 | @Mock 28 | CanExecuteProcessInContainer canExecuteProcessInContainer; 29 | 30 | @Before 31 | public void configureMocks() { 32 | when(cube.hasMetadata(CanExecuteProcessInContainer.class)).thenReturn(true); 33 | when(cube.getMetadata(CanExecuteProcessInContainer.class)).thenReturn(canExecuteProcessInContainer); 34 | } 35 | 36 | @Test 37 | public void shouldBlockPort() { 38 | final BlockPortSimianArmyChaosScript blockPortSimianArmyChaosScript = 39 | new BlockPortSimianArmyChaosScript(new Integer[] {80, 8080}); 40 | blockPortSimianArmyChaosScript.apply(cube); 41 | 42 | verify(canExecuteProcessInContainer).exec("iptables", "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", "80", 43 | "-j", "DROP"); 44 | verify(canExecuteProcessInContainer).exec("iptables", "-A", "INPUT", "-p", "udp", "-m", "udp", "--dport", "80", 45 | "-j", "DROP"); 46 | verify(canExecuteProcessInContainer).exec("iptables", "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", "8080", 47 | "-j", "DROP"); 48 | verify(canExecuteProcessInContainer).exec("iptables", "-A", "INPUT", "-p", "udp", "-m", "udp", "--dport", "8080", 49 | "-j", "DROP"); 50 | } 51 | 52 | @Test 53 | public void shouldBurnCpu() { 54 | final BurnCpuSimianArmyChaosScript burnCpuSimianArmyChaosScript = new BurnCpuSimianArmyChaosScript(2); 55 | burnCpuSimianArmyChaosScript.apply(cube); 56 | 57 | verify(canExecuteProcessInContainer, times(2)).exec("dd", "if=/dev/zero", "of=/dev/null"); 58 | } 59 | 60 | @Test 61 | public void shouldBurnIo() { 62 | BurnIoSimianArmyChaosScript burnIoSimianArmyChaosScript = new BurnIoSimianArmyChaosScript(); 63 | burnIoSimianArmyChaosScript.apply(cube); 64 | 65 | verify(canExecuteProcessInContainer).exec("dd", "if=/dev/urandom", "of=/burn", "bs=1M", "count=1024", 66 | "iflag=fullblock"); 67 | } 68 | 69 | @Test 70 | public void shouldFillDisk() { 71 | final FillDiskSimianArmyChaosScript fillDiskSimianArmyChaosScript = new FillDiskSimianArmyChaosScript(10000); 72 | fillDiskSimianArmyChaosScript.apply(cube); 73 | 74 | verify(canExecuteProcessInContainer).exec("dd", "if=/dev/urandom", "of=/burn", "bs=1M", "count=10000", 75 | "iflag=fullblock"); 76 | } 77 | 78 | @Test 79 | public void shouldKillProcess() { 80 | KillProcessSimianArmyChaosScript killProcessSimianArmyChaosScript = new KillProcessSimianArmyChaosScript("java"); 81 | killProcessSimianArmyChaosScript.apply(cube); 82 | 83 | verify(canExecuteProcessInContainer).exec("pkill", "-f", "java"); 84 | } 85 | 86 | @Test 87 | public void shouldNullRoute() { 88 | NullRouteSimianArmyChaosScript nullRouteSimianArmyChaosScript = new NullRouteSimianArmyChaosScript(); 89 | nullRouteSimianArmyChaosScript.apply(cube); 90 | 91 | verify(canExecuteProcessInContainer).exec("ip", "route", "add", "blackhole", "10.0.0.0/8"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ftest-toxic-reporter/src/test/java/org/arquillian/cube/q/toxic/reporter/ToxicFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.reporter; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | import org.arquillian.cube.HostIp; 5 | import org.arquillian.cube.impl.util.IOUtil; 6 | import org.arquillian.cube.q.api.NetworkChaos; 7 | import org.jboss.arquillian.junit.Arquillian; 8 | import org.jboss.arquillian.test.api.ArquillianResource; 9 | import org.junit.After; 10 | import org.junit.AfterClass; 11 | import org.junit.BeforeClass; 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | import org.junit.rules.ExpectedException; 15 | import org.junit.rules.TestName; 16 | import org.junit.runner.RunWith; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.net.SocketException; 21 | import java.net.URL; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import static org.arquillian.cube.q.api.NetworkChaos.DistributedLatencyType.logNormalLatencyInMillis; 26 | import static org.arquillian.cube.q.api.NetworkChaos.RateType.rate; 27 | import static org.arquillian.cube.q.api.NetworkChaos.TimeoutType.timeoutInMillis; 28 | import static org.arquillian.cube.q.api.Q.IterationRunCondition.times; 29 | import static org.assertj.core.api.Assertions.assertThat; 30 | 31 | @RunWith(Arquillian.class) 32 | public class ToxicFunctionalTest { 33 | 34 | @ArquillianResource 35 | private NetworkChaos networkChaos; 36 | 37 | @HostIp 38 | private String ip; 39 | 40 | @Rule 41 | public TestName name = new TestName(); 42 | 43 | @Rule 44 | public ExpectedException thrown = ExpectedException.none(); 45 | 46 | private static List executedMethods = new ArrayList<>(); 47 | 48 | private static final String LOG_DIR = (System.getProperty("user.dir") + "/target/reports/chaos/"); 49 | 50 | @BeforeClass 51 | public static void clean_json_files_dir_from_target_reports() throws IOException { 52 | File chaosDir = new File(LOG_DIR); 53 | if (chaosDir.exists()) { 54 | FileUtils.cleanDirectory(new File(LOG_DIR)); 55 | } 56 | } 57 | 58 | @Test 59 | public void should_add_timeout() throws Exception { 60 | thrown.expect(SocketException.class); 61 | thrown.expectMessage("Unexpected end of file from server"); 62 | networkChaos.on("hw", 8080).timeout(timeoutInMillis(1000)).exec(() -> { 63 | getResponse(); 64 | }); 65 | } 66 | 67 | @Test 68 | public void should_add_bandwidth_with_iterations() throws Exception { 69 | networkChaos.on("hw", 8080).bandwidth(rate(1000)).exec(times(3), () -> { 70 | getResponse(); 71 | }); 72 | } 73 | 74 | @Test 75 | public void should_add_log_normal_latency_and_bandwidth_with_iterations() throws Exception { 76 | NetworkChaos.Action chaosAction = networkChaos.on("hw", 8080); 77 | 78 | chaosAction.bandwidth(NetworkChaos.DistributedRateType.logNormalLatencyInMillis(3000, 0.2)); 79 | chaosAction.latency(logNormalLatencyInMillis(2000, 0.3)); 80 | 81 | chaosAction.exec(times(3), () -> { 82 | getResponse(); 83 | }); 84 | } 85 | 86 | @After 87 | public void add_method_name() { 88 | executedMethods.add(name.getMethodName() + ".json"); 89 | } 90 | 91 | @AfterClass 92 | public static void verify_json_files_in_reports_dir() { 93 | File chaosDir = new File(LOG_DIR); 94 | final int size = executedMethods.size(); 95 | final String[] fileNames = executedMethods.toArray(new String[executedMethods.size()]); 96 | 97 | assertThat(chaosDir.exists()).isTrue(); 98 | assertThat(chaosDir.list()).hasSize(size).containsExactlyInAnyOrder(fileNames); 99 | } 100 | 101 | private void getResponse() throws IOException { 102 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 103 | final long l = System.currentTimeMillis(); 104 | String response = IOUtil.asString(url.openStream()); 105 | System.out.println(response); 106 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/client/ToxiProxyScenario.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic.client; 2 | 3 | import eu.rekawek.toxiproxy.Proxy; 4 | import org.arquillian.cube.q.api.Q; 5 | import org.arquillian.cube.q.toxic.event.ToxicCreated; 6 | import org.arquillian.cube.q.toxic.event.ToxicUpdated; 7 | import org.jboss.arquillian.core.api.Event; 8 | import org.jboss.arquillian.core.api.Injector; 9 | import org.jboss.arquillian.core.api.annotation.Inject; 10 | 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.logging.Level; 15 | import java.util.logging.Logger; 16 | 17 | public class ToxiProxyScenario implements ToxiProxy { 18 | 19 | private static final Logger logger = Logger.getLogger(ToxiProxyScenario.class.getName()); 20 | 21 | private ToxiProxyClient client; 22 | 23 | private Map proxies; 24 | 25 | private Injector injector; 26 | 27 | public ToxiProxyScenario(ToxiProxyClient client, Injector injector) { 28 | this.client = client; 29 | this.proxies = new HashMap<>(); 30 | this.injector = injector; 31 | } 32 | 33 | public ToxiProxyScenario(ToxiProxyClient client) { 34 | this.client = client; 35 | this.proxies = new HashMap<>(); 36 | } 37 | 38 | public void register(String name, String listen, String upstream) { 39 | proxies.put(name, client.createProxy(name, listen, upstream)); 40 | } 41 | 42 | public void reset() { 43 | client.reset(); 44 | proxies = client.getProxies(); 45 | } 46 | 47 | public Scenario given(String name) { 48 | if (!proxies.containsKey(name)) { 49 | throw new IllegalArgumentException("No known proxy with name " + name); 50 | } 51 | 52 | ToxicScenario toxicScenario = new ToxicScenario(proxies.get(name)); 53 | 54 | return injector.inject(toxicScenario); 55 | } 56 | 57 | public class ToxicScenario implements Scenario { 58 | 59 | private Proxy proxy; 60 | private List toxics; 61 | 62 | @Inject 63 | private Event toxicCreated; 64 | 65 | @Inject 66 | private Event toxicUpdated; 67 | 68 | public ToxicScenario(Proxy proxy) { 69 | this.proxy = proxy; 70 | } 71 | 72 | @Override 73 | public Scenario given(String name) { 74 | return ToxiProxyScenario.this.given(name); 75 | } 76 | 77 | @Override 78 | public Scenario using(final List toxics) { 79 | this.toxics = toxics; 80 | return this; 81 | } 82 | 83 | @Override 84 | public void then(Callable callable) throws Exception { 85 | try { 86 | execute(); 87 | callable.call(); 88 | } finally { 89 | reset(); 90 | } 91 | } 92 | 93 | @Override 94 | public void then(Q.RunCondition runCondition, Callable callable) throws Exception { 95 | try { 96 | execute(runCondition); 97 | callable.call(); 98 | } finally { 99 | reset(); 100 | } 101 | } 102 | 103 | @Override 104 | public void execute() throws Exception { 105 | for (ToxiProxyClient.BaseToxic toxic : toxics) { 106 | logger.log(Level.FINER, String.format("Next toxic is created %s.", toxic.toString())); 107 | client.createToxic(proxy, toxic); 108 | toxicCreated.fire(new ToxicCreated(toxic)); 109 | } 110 | } 111 | 112 | @Override 113 | public void execute(Q.RunCondition runCondition) throws Exception { 114 | for (ToxiProxyClient.BaseToxic toxic : toxics) { 115 | logger.log(Level.FINER, String.format("Next toxic is created %s.", toxic.toString())); 116 | client.createToxic(proxy, toxic); 117 | toxicCreated.fire(new ToxicCreated(toxic, runCondition)); 118 | } 119 | } 120 | 121 | @Override 122 | public void update() throws Exception { 123 | for (ToxiProxyClient.BaseToxic toxic : toxics) { 124 | logger.log(Level.FINER, String.format("Next toxic is updated %s.", toxic.toString())); 125 | client.updateToxic(proxy, toxic); 126 | toxicUpdated.fire(new ToxicUpdated(toxic)); 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /api/src/main/java/org/arquillian/cube/q/api/Q.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.api; 2 | 3 | import java.util.Arrays; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | public interface Q { 7 | 8 | void exec() throws Exception; 9 | 10 | void exec(Perform perform) throws Exception; 11 | 12 | void exec(RunCondition runCondition, Perform perform) throws Exception; 13 | 14 | interface Perform { 15 | void execute() throws Exception; 16 | } 17 | 18 | interface RunCondition { 19 | boolean isExecutable(); 20 | } 21 | 22 | class DurationRunCondition implements Q.RunCondition { 23 | 24 | private long finishTime; 25 | 26 | protected DurationRunCondition(long duration, TimeUnit unit) { 27 | final long durationInMillis = unit.toMillis(duration); 28 | this.finishTime = System.currentTimeMillis() + durationInMillis; 29 | } 30 | 31 | public long getFinishTime() { 32 | return finishTime; 33 | } 34 | 35 | public static DurationRunCondition during(long duration, TimeUnit timeUnit) { 36 | return new DurationRunCondition(duration, timeUnit); 37 | } 38 | 39 | @Override 40 | public boolean isExecutable() { 41 | return System.currentTimeMillis() < finishTime; 42 | } 43 | } 44 | 45 | class IterationRunCondition implements Q.RunCondition { 46 | private final long iterations; 47 | protected long currentIteration = 0; 48 | 49 | protected IterationRunCondition(long iterations) { 50 | this.iterations = iterations; 51 | } 52 | 53 | public long getIterations() { 54 | return iterations; 55 | } 56 | 57 | public static IterationRunCondition times(long numberOfIterations) { 58 | return new IterationRunCondition(numberOfIterations); 59 | } 60 | 61 | @Override 62 | public boolean isExecutable() { 63 | 64 | if (currentIteration < iterations) { 65 | currentIteration++; 66 | return true; 67 | } else { 68 | return false; 69 | } 70 | } 71 | } 72 | 73 | abstract class BaseType { 74 | private boolean distributed = false; 75 | protected T value; 76 | 77 | protected BaseType(boolean distributed, T value) { 78 | this.distributed = distributed; 79 | this.value = value; 80 | } 81 | 82 | protected void setDistributed() { 83 | this.distributed = true; 84 | } 85 | 86 | public boolean isDistributed() { 87 | return this.distributed; 88 | } 89 | 90 | public void calculateValue() { 91 | } 92 | 93 | public T getValue() { 94 | return value; 95 | } 96 | } 97 | 98 | abstract class FloatType extends BaseType { 99 | public FloatType(float value) { 100 | super(false, value); 101 | this.value = value; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return Float.toString(value); 107 | } 108 | } 109 | 110 | abstract class LongType extends BaseType { 111 | protected LongType(long value) { 112 | super(false, value); 113 | this.value = value; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | return Long.toString(value); 119 | } 120 | } 121 | 122 | abstract class IntegerType extends BaseType { 123 | protected IntegerType(int value) { 124 | super(false, value); 125 | this.value = value; 126 | } 127 | 128 | @Override 129 | public String toString() { 130 | return Integer.toString(value); 131 | } 132 | } 133 | 134 | abstract class StringType extends BaseType { 135 | protected StringType(String value) { 136 | super(false, value); 137 | this.value = value; 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return value; 143 | } 144 | } 145 | 146 | abstract class ArrayType extends BaseType { 147 | 148 | protected ArrayType(T[] value) { 149 | super(false, value); 150 | this.value = value; 151 | } 152 | 153 | @Override 154 | public String toString() { 155 | return Arrays.toString(value); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /ftest-toxic/src/test/java/org/arquillian/cube/q/toxic/ToxicFunctionalTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import eu.rekawek.toxiproxy.Proxy; 4 | import eu.rekawek.toxiproxy.ToxiproxyClient; 5 | import org.arquillian.cube.HostIp; 6 | import org.arquillian.cube.docker.impl.requirement.RequiresDockerMachine; 7 | import org.arquillian.cube.impl.util.IOUtil; 8 | import org.arquillian.cube.q.api.NetworkChaos; 9 | import org.arquillian.cube.requirement.ArquillianConditionalRunner; 10 | import org.jboss.arquillian.test.api.ArquillianResource; 11 | import org.junit.Assert; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | import java.io.IOException; 16 | import java.net.URL; 17 | 18 | import static org.arquillian.cube.q.api.NetworkChaos.DistributedLatencyType.logNormalLatencyInMillis; 19 | import static org.arquillian.cube.q.api.NetworkChaos.LatencyType.latencyInMillis; 20 | import static org.arquillian.cube.q.api.Q.IterationRunCondition.times; 21 | import static org.hamcrest.CoreMatchers.is; 22 | 23 | @RequiresDockerMachine(name = "dev") 24 | @RunWith(ArquillianConditionalRunner.class) 25 | public class ToxicFunctionalTestCase { 26 | 27 | @ArquillianResource 28 | private NetworkChaos networkChaos; 29 | 30 | @HostIp 31 | private String ip; 32 | 33 | @Test 34 | public void should() throws Exception { 35 | 36 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 37 | final long l = System.currentTimeMillis(); 38 | String response = IOUtil.asString(url.openStream()); 39 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 40 | Assert.assertNotNull(response); 41 | } 42 | 43 | @Test 44 | public void shouldAddLatency() throws Exception { 45 | networkChaos.on("pingpong", 8080).latency(latencyInMillis(4000)).exec(() -> { 46 | 47 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 48 | final long l = System.currentTimeMillis(); 49 | String response = IOUtil.asString(url.openStream()); 50 | System.out.println(response); 51 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 52 | }); 53 | } 54 | 55 | @Test 56 | public void shouldAddLatencyWithExec() throws Exception { 57 | networkChaos.on("pingpong", 8080).latency(latencyInMillis(4000)).exec(); 58 | 59 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 60 | final long l = System.currentTimeMillis(); 61 | String response = IOUtil.asString(url.openStream()); 62 | System.out.println(response); 63 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 64 | 65 | ToxiproxyClient client = new ToxiproxyClient(ip, 8474); 66 | final Proxy proxy = client.getProxy("pingpong:8080"); 67 | Assert.assertThat(proxy.toxics().getAll().size(), is(1)); 68 | } 69 | 70 | @Test 71 | public void shouldAddLogNormalLatencyWithIterations() throws Exception { 72 | networkChaos.on("pingpong", 8080).latency(logNormalLatencyInMillis(2000, 0.3)).exec(times(2), () -> { 73 | 74 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 75 | final long l = System.currentTimeMillis(); 76 | String response = IOUtil.asString(url.openStream()); 77 | System.out.println(response); 78 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 79 | }); 80 | } 81 | 82 | @Test 83 | public void shouldAddLatencyWithIterations() throws Exception { 84 | networkChaos.on("pingpong", 8080).latency(latencyInMillis(4000)).exec(times(2), () -> { 85 | 86 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 87 | final long l = System.currentTimeMillis(); 88 | String response = IOUtil.asString(url.openStream()); 89 | System.out.println(response); 90 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 91 | }); 92 | } 93 | 94 | @Test(expected = IOException.class) 95 | public void shouldAddDownToxic() throws Exception { 96 | networkChaos.on("pingpong", 8080).down().exec(() -> { 97 | URL url = new URL("http://" + ip + ":" + 8081 + "/hw/HelloWorld"); 98 | final long l = System.currentTimeMillis(); 99 | String response = IOUtil.asString(url.openStream()); 100 | System.out.println(response); 101 | System.out.println("Time:" + (System.currentTimeMillis() - l)); 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /toxic/src/test/java/org/arquillian/cube/q/toxic/InstallProxyTestCase.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 4 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 5 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 6 | import org.arquillian.cube.docker.impl.client.config.ExposedPort; 7 | import org.arquillian.cube.docker.impl.client.config.Link; 8 | import org.arquillian.cube.docker.impl.client.config.PortBinding; 9 | import org.arquillian.cube.q.core.InstallProxy; 10 | import org.arquillian.cube.q.spi.NetworkChaosConfiguration; 11 | import org.arquillian.cube.q.spi.ProxyManager; 12 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 13 | import org.jboss.arquillian.core.spi.ServiceLoader; 14 | import org.jboss.arquillian.core.test.AbstractManagerTestBase; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.junit.runner.RunWith; 18 | import org.mockito.Mock; 19 | import org.mockito.Mockito; 20 | import org.mockito.runners.MockitoJUnitRunner; 21 | 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | 28 | @RunWith(MockitoJUnitRunner.class) 29 | public class InstallProxyTestCase extends AbstractManagerTestBase { 30 | 31 | private static final String CONTENT = 32 | "a:\n" + 33 | " image: a/a\n" + 34 | " portBindings: [8089/tcp]\n" + 35 | " links:\n" + 36 | " - b:b\n" + 37 | "b:\n" + 38 | " image: b/b\n" + 39 | " exposedPorts: [2112/tcp]\n"; 40 | 41 | @Mock 42 | private ServiceLoader loader; 43 | 44 | @Mock 45 | NetworkChaosConfiguration networkChaosConfiguration; 46 | 47 | private ToxicProxyHandler t; 48 | 49 | @Override 50 | protected void addExtensions(List> extensions) { 51 | extensions.add(InstallProxy.class); 52 | } 53 | 54 | @Before 55 | public void setup() { 56 | t = new ToxicProxyHandler(); 57 | t.networkChaosConfigurationInstance = () -> { 58 | Mockito.when(networkChaosConfiguration.isToxifyPortBinding()).thenReturn(false); 59 | return networkChaosConfiguration; 60 | }; 61 | Mockito.when(loader.onlyOne(ProxyManager.class)).thenReturn(t); 62 | bind(ApplicationScoped.class, ServiceLoader.class, loader); 63 | } 64 | 65 | @Test 66 | public void shouldInstallProxy() throws Exception { 67 | CubeDockerConfiguration config = createConfig(CONTENT); 68 | fire(config); 69 | 70 | DockerCompositions cubes = config.getDockerContainersContent(); 71 | assertThat(cubes.getContainerIds()).hasSize(3); 72 | } 73 | 74 | @Test 75 | public void shouldRedirectLinksToToxicProxy() { 76 | CubeDockerConfiguration config = createConfig(CONTENT); 77 | fire(config); 78 | 79 | DockerCompositions cubes = config.getDockerContainersContent(); 80 | CubeContainer a = cubes.get("a"); 81 | assertThat(a.getLinks()).containsExactlyInAnyOrder(new Link("toxiproxy", "b"), 82 | new Link("toxiproxy", "toxiproxy")); 83 | 84 | CubeContainer b = cubes.get("toxiproxy"); 85 | assertThat(b.getLinks()).containsExactlyInAnyOrder(new Link("b", "b_toxiproxy")); 86 | } 87 | 88 | @Test 89 | public void shouldRedirtPortBindingToToxicProxy() { 90 | 91 | t = new ToxicProxyHandler(); 92 | t.networkChaosConfigurationInstance = () -> { 93 | Mockito.when(networkChaosConfiguration.isToxifyPortBinding()).thenReturn(true); 94 | return networkChaosConfiguration; 95 | }; 96 | Mockito.when(loader.onlyOne(ProxyManager.class)).thenReturn(t); 97 | 98 | CubeDockerConfiguration config = createConfig(CONTENT); 99 | fire(config); 100 | 101 | DockerCompositions cubes = config.getDockerContainersContent(); 102 | CubeContainer a = cubes.get("a"); 103 | System.out.println(config.toString()); 104 | assertThat(a.getPortBindings()).isNullOrEmpty(); 105 | assertThat(a.getExposedPorts()).containsExactlyInAnyOrder(ExposedPort.valueOf("8089/tcp")); 106 | 107 | CubeContainer b = cubes.get("toxiproxy"); 108 | assertThat(b.getLinks()).containsExactlyInAnyOrder(new Link("a", "a_toxiproxy")); 109 | assertThat(b.getPortBindings()).containsExactlyInAnyOrder(PortBinding.valueOf("8474/tcp"), 110 | PortBinding.valueOf("8089/tcp")); 111 | } 112 | 113 | private CubeDockerConfiguration createConfig(String content) { 114 | Map parameters = new HashMap<>(); 115 | 116 | parameters.put("serverVersion", "1.13"); 117 | parameters.put("serverUri", "http://localhost:25123"); 118 | parameters.put("definitionFormat", "CUBE"); 119 | parameters.put("dockerContainers", content); 120 | parameters.put("definitionFormat", "CUBE"); 121 | 122 | return CubeDockerConfiguration.fromMap(parameters, null); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* 125 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 126 | if ERRORLEVEL 1 goto error 127 | goto end 128 | 129 | :error 130 | set ERROR_CODE=1 131 | 132 | :end 133 | @endlocal & set ERROR_CODE=%ERROR_CODE% 134 | 135 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 136 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 137 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 138 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 139 | :skipRcPost 140 | 141 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 142 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 143 | 144 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 145 | 146 | exit /B %ERROR_CODE% 147 | -------------------------------------------------------------------------------- /reporter/src/main/java/org/arquillian/cube/q/recorder/TakeNetworkChaosInformation.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.recorder; 2 | 3 | import com.fasterxml.jackson.core.JsonEncoding; 4 | import com.fasterxml.jackson.core.JsonFactory; 5 | import com.fasterxml.jackson.core.JsonGenerator; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.node.ArrayNode; 8 | import com.fasterxml.jackson.databind.node.JsonNodeFactory; 9 | import com.fasterxml.jackson.databind.node.ObjectNode; 10 | import org.arquillian.cube.q.api.Q; 11 | import org.arquillian.cube.q.toxic.QNetworkChaosToxic; 12 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 13 | import org.arquillian.cube.q.toxic.event.ToxicCreated; 14 | import org.arquillian.cube.q.toxic.event.ToxicUpdated; 15 | import org.arquillian.reporter.api.builder.Reporter; 16 | import org.arquillian.reporter.api.event.SectionEvent; 17 | import org.arquillian.reporter.api.event.TestMethodSection; 18 | import org.arquillian.reporter.api.model.entry.FileEntry; 19 | import org.arquillian.reporter.api.model.report.TestMethodReport; 20 | import org.arquillian.reporter.config.ReporterConfiguration; 21 | import org.jboss.arquillian.core.api.Event; 22 | import org.jboss.arquillian.core.api.annotation.Inject; 23 | import org.jboss.arquillian.core.api.annotation.Observes; 24 | import org.jboss.arquillian.test.spi.annotation.TestScoped; 25 | import org.jboss.arquillian.test.spi.event.suite.After; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.lang.reflect.Method; 30 | import java.nio.file.Files; 31 | import java.nio.file.Path; 32 | import java.nio.file.Paths; 33 | import java.util.ArrayList; 34 | import java.util.LinkedHashMap; 35 | import java.util.List; 36 | import java.util.Map; 37 | 38 | import static org.arquillian.cube.q.recorder.NetworkChaosInformationReportKey.TOXICITY_DETAILS_PATH; 39 | 40 | /** 41 | * Class that reports general information about network toxics. 42 | */ 43 | public class TakeNetworkChaosInformation { 44 | 45 | private static final String CREATE = "create"; 46 | private static final String UPDATE = "update"; 47 | 48 | @Inject 49 | Event sectionEvent; 50 | 51 | @TestScoped 52 | private List> toxics = new ArrayList<>(); 53 | 54 | public void captureToxicDetailsAfterCreate(@Observes ToxicCreated toxicCreatedEvent, 55 | QNetworkChaosToxic.ToxicAction toxicAction) { 56 | final ToxiProxyClient.BaseToxic toxic = toxicCreatedEvent.getToxic(); 57 | final Q.RunCondition runCondition = toxicCreatedEvent.getRunCondition(); 58 | addToxicInfoToToxics(toxicAction, toxic, runCondition, CREATE); 59 | } 60 | 61 | public void captureToxicDetailsAfterUpdate(@Observes ToxicUpdated toxicUpdatedEvent, 62 | QNetworkChaosToxic.ToxicAction toxicAction) { 63 | final ToxiProxyClient.BaseToxic toxic = toxicUpdatedEvent.getToxic(); 64 | addToxicInfoToToxics(toxicAction, toxic, null, UPDATE); 65 | } 66 | 67 | public void addToxicInfoToToxics(QNetworkChaosToxic.ToxicAction toxicAction, ToxiProxyClient.BaseToxic toxic, 68 | Q.RunCondition runCondition, String phase) { 69 | final String actionOn = toxicAction.getName(); 70 | final String toxicType = toxic.getClass().getSimpleName(); 71 | 72 | Map toxicInfo = new LinkedHashMap<>(); 73 | toxicInfo.put("actionon", actionOn); 74 | toxicInfo.put("type", toxicType); 75 | toxicInfo.put("phase", phase); 76 | toxicInfo.put("toxic", toxic); 77 | if (runCondition != null) { 78 | toxicInfo.put("runcondition", runCondition); 79 | } 80 | 81 | toxics.add(toxicInfo); 82 | } 83 | 84 | public void reportToxicConfiguration(@Observes After event, ReporterConfiguration reporterConfiguration) 85 | throws IOException { 86 | 87 | final Method testMethod = event.getTestMethod(); 88 | final String testMethodName = testMethod.getName(); 89 | final String fileName = testMethodName + ".json"; 90 | 91 | final FileEntry fileEntry = createFileEntryWithJSON(reporterConfiguration, fileName); 92 | Reporter.createReport(new TestMethodReport(testMethodName)) 93 | .addKeyValueEntry(TOXICITY_DETAILS_PATH, fileEntry) 94 | .inSection(new TestMethodSection(testMethod)) 95 | .fire(sectionEvent); 96 | 97 | toxics.clear(); 98 | } 99 | 100 | private FileEntry createFileEntryWithJSON(ReporterConfiguration reporterConfiguration, String fileName) 101 | throws IOException { 102 | final File rootDirectory = new File(reporterConfiguration.getRootDirectory()); 103 | File jsonFile = new File(createDirectory(rootDirectory, "chaos"), fileName); 104 | 105 | if (!toxics.isEmpty()) { 106 | createJSONAndWriteToFile(jsonFile); 107 | } 108 | 109 | final Path rootDir = Paths.get(rootDirectory.getName()); 110 | final Path relativize = rootDir.relativize(jsonFile.toPath()); 111 | 112 | return new FileEntry(relativize.toString()); 113 | } 114 | 115 | private void createJSONAndWriteToFile(File file) throws IOException { 116 | JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false); 117 | JsonFactory jsonFactory = new JsonFactory(); 118 | JsonGenerator generator = jsonFactory.createGenerator(file, JsonEncoding.UTF8); 119 | 120 | ObjectMapper mapper = new ObjectMapper(); 121 | ArrayNode toxic = mapper.convertValue(toxics, ArrayNode.class); 122 | 123 | ObjectNode root = jsonNodeFactory.objectNode(); 124 | root.set("services", toxic); 125 | 126 | mapper.writeTree(generator, root); 127 | } 128 | 129 | private File createDirectory(File rootDirectory, String name) { 130 | final Path reportChaos = Paths.get("reports", name); 131 | final Path chaosDir = rootDirectory.toPath().resolve(reportChaos); 132 | 133 | try { 134 | Files.createDirectories(chaosDir); 135 | } catch (IOException e) { 136 | throw new IllegalArgumentException(String.format("Could not created chaos directory at %s", chaosDir)); 137 | } 138 | 139 | return chaosDir.toFile(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /spi/src/main/java/org/arquillian/cube/q/spi/Proxy.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.spi; 2 | 3 | import org.arquillian.cube.docker.impl.client.config.Await; 4 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 5 | import org.arquillian.cube.docker.impl.client.config.ExposedPort; 6 | import org.arquillian.cube.docker.impl.client.config.Image; 7 | import org.arquillian.cube.docker.impl.client.config.Link; 8 | import org.arquillian.cube.docker.impl.client.config.PortBinding; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.HashMap; 14 | import java.util.HashSet; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Set; 18 | 19 | public class Proxy { 20 | 21 | private String name; 22 | private ExposedPort communicationPort; 23 | private CubeContainer cube; 24 | private Collection relations; 25 | 26 | private Proxy(String name, ExposedPort communicationPort, CubeContainer cube, Collection relations) { 27 | this.name = name; 28 | this.communicationPort = communicationPort; 29 | this.cube = cube; 30 | this.relations = relations; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public ExposedPort getCommunicationPort() { 38 | return communicationPort; 39 | } 40 | 41 | public CubeContainer getCube() { 42 | return cube; 43 | } 44 | 45 | public Collection getRelations() { 46 | return relations; 47 | } 48 | 49 | public Collection getRelations(String to) { 50 | List relations = new ArrayList<>(); 51 | for (Relation rel : getRelations()) { 52 | if (rel.getTo().equals(to)) { 53 | relations.add(rel); 54 | } 55 | } 56 | return relations; 57 | } 58 | 59 | public static Builder create() { 60 | return new Builder(); 61 | } 62 | 63 | public static class Relation { 64 | private String from; 65 | private String to; 66 | private ExposedPort port; 67 | 68 | public Relation(String from, String to, ExposedPort port) { 69 | this.from = from; 70 | this.to = to; 71 | this.port = port; 72 | } 73 | 74 | public String getFrom() { 75 | return from; 76 | } 77 | 78 | public String getTo() { 79 | return to; 80 | } 81 | 82 | public ExposedPort getPort() { 83 | return port; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return "Relation [from=" + from + ", to=" + to + ", port=" + port + "]"; 89 | } 90 | } 91 | 92 | public static class Builder { 93 | private final String DEFAULT_NAME = "toxiproxy"; 94 | private final int DEFAULT_PORT = 8474; 95 | 96 | private String image = "shopify/toxiproxy"; 97 | private Set expose = new HashSet<>(); 98 | private Set bind = new HashSet<>(); 99 | 100 | private Map> containerExpose = new HashMap<>(); 101 | private Map> containerLinks = new HashMap<>(); 102 | 103 | public Builder() { 104 | } 105 | 106 | public Builder containerExpose(String containerName, int exposed, String protocol) { 107 | exposePort(exposed, protocol); 108 | getValue(containerName, containerExpose).add(exposed + "/" + protocol); 109 | return this; 110 | } 111 | 112 | public Builder containerBinds(String containerName, int bound, int exposed, String protocol) { 113 | bindPort(bound, exposed, protocol); 114 | getValue(containerName, containerExpose).add(exposed + "/" + protocol); 115 | containerLinks(containerName, containerName + "_toxiproxy"); 116 | return this; 117 | } 118 | 119 | public Builder containerLinks(String containerFrom, String containerTo) { 120 | getValue(containerFrom, containerLinks).add(containerTo); 121 | return this; 122 | } 123 | 124 | private Builder exposePort(int expose, String protocol) { 125 | this.expose.add(expose + "/" + protocol); 126 | return this; 127 | } 128 | 129 | private Builder bindPort(int bound, int exposed, String protocol) { 130 | bind.add(bound + "->" + exposed + "/" + protocol); 131 | return this; 132 | } 133 | 134 | public Proxy build() { 135 | bindPort(DEFAULT_PORT, DEFAULT_PORT, "tcp"); 136 | 137 | CubeContainer cube = new CubeContainer(); 138 | cube.setImage(Image.valueOf(image)); 139 | cube.setExposedPorts(ExposedPort.valuesOf(expose)); 140 | cube.setPortBindings(PortBinding.valuesOf(bind)); 141 | Await await = new Await(); 142 | await.setStrategy("polling"); 143 | await.setType("ping"); 144 | await.setPorts(Collections.singletonList(DEFAULT_PORT)); 145 | cube.setAwait(await); 146 | cube.setRemoveVolumes(true); 147 | cube.setLinks(buildUniqueLinks()); 148 | 149 | return new Proxy(getName(), ExposedPort.valueOf(DEFAULT_PORT + "/tcp"), cube, buildRelations()); 150 | } 151 | 152 | private Collection buildUniqueLinks() { 153 | List unique = new ArrayList<>(); 154 | for (Map.Entry> links : containerLinks.entrySet()) { 155 | final List linksValue = links.getValue(); 156 | for (String linkValue : linksValue) { 157 | unique.add(new Link(links.getKey(), linkValue)); 158 | } 159 | } 160 | return unique; 161 | } 162 | 163 | private Collection buildRelations() { 164 | List relations = new ArrayList<>(); 165 | for (Map.Entry> links : containerLinks.entrySet()) { 166 | for (String linkedTo : links.getValue()) { 167 | List exposed = containerExpose.get(links.getKey()); 168 | if (exposed == null) { 169 | continue; 170 | } 171 | for (String port : exposed) { 172 | relations.add(new Relation(links.getKey(), linkedTo, ExposedPort.valueOf(port))); 173 | } 174 | } 175 | } 176 | return relations; 177 | } 178 | 179 | private List getValue(String key, Map> map) { 180 | if (!map.containsKey(key)) { 181 | map.put(key, new ArrayList<>()); 182 | } 183 | return map.get(key); 184 | } 185 | 186 | public String getName() { 187 | return DEFAULT_NAME; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /pumba/src/main/java/org/arquillian/cube/q/pumba/QPumbaAction.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.pumba; 2 | 3 | import org.arquillian.cube.CubeController; 4 | import org.arquillian.cube.docker.impl.client.CubeDockerConfiguration; 5 | import org.arquillian.cube.docker.impl.model.DockerCube; 6 | import org.arquillian.cube.q.api.ContainerChaos; 7 | import org.arquillian.cube.q.pumba.util.PumbaCommandLineCreator; 8 | import org.arquillian.cube.q.spi.StandaloneContainer; 9 | import org.arquillian.cube.spi.Cube; 10 | import org.arquillian.cube.spi.CubeRegistry; 11 | 12 | public class QPumbaAction implements ContainerChaos.Action { 13 | 14 | private final CubeController cubeController; 15 | private final CubeRegistry cubeRegistry; 16 | private final CubeDockerConfiguration cubeDockerConfiguration; 17 | 18 | public QPumbaAction(CubeController cubeController, CubeRegistry cubeRegistry, 19 | CubeDockerConfiguration cubeDockerConfiguration) { 20 | this.cubeController = cubeController; 21 | this.cubeRegistry = cubeRegistry; 22 | this.cubeDockerConfiguration = cubeDockerConfiguration; 23 | } 24 | 25 | @Override 26 | public ContainerChaos.Action stop(ContainerChaos.ContainersType containersType, 27 | ContainerChaos.IntervalType intervalType) { 28 | configurePumbaCube(PumbaChaosCommandBuilder.create() 29 | .containers(containersType) 30 | .interval(intervalType) 31 | .chaosOperation(ChaosOperation.STOP) 32 | , false); 33 | return this; 34 | } 35 | 36 | @Override 37 | public ContainerChaos.Action stopRandomly(ContainerChaos.ContainersType containersType, 38 | ContainerChaos.IntervalType intervalType) { 39 | configurePumbaCube(PumbaChaosCommandBuilder.create() 40 | .containers(containersType) 41 | .interval(intervalType) 42 | .chaosOperation(ChaosOperation.STOP) 43 | , true); 44 | return this; 45 | } 46 | 47 | @Override 48 | public ContainerChaos.Action remove(ContainerChaos.ContainersType containersType, 49 | ContainerChaos.IntervalType intervalType) { 50 | configurePumbaCube(PumbaChaosCommandBuilder.create() 51 | .containers(containersType) 52 | .interval(intervalType) 53 | .chaosOperation(ChaosOperation.RM) 54 | , false); 55 | return this; 56 | } 57 | 58 | @Override 59 | public ContainerChaos.Action removeRandomly(ContainerChaos.ContainersType containersType, 60 | ContainerChaos.IntervalType intervalType) { 61 | configurePumbaCube(PumbaChaosCommandBuilder.create() 62 | .containers(containersType) 63 | .interval(intervalType) 64 | .chaosOperation(ChaosOperation.RM) 65 | , true); 66 | return this; 67 | } 68 | 69 | @Override 70 | public ContainerChaos.Action kill(ContainerChaos.ContainersType containersType, 71 | ContainerChaos.IntervalType intervalType, ContainerChaos.KillSignal killSignal) { 72 | configurePumbaCube(PumbaChaosCommandBuilder.create() 73 | .containers(containersType) 74 | .interval(intervalType) 75 | .chaosOperation(ChaosOperation.KILL) 76 | .killSignal(killSignal) 77 | , false); 78 | return this; 79 | } 80 | 81 | @Override 82 | public ContainerChaos.Action killRandomly(ContainerChaos.ContainersType containersType, 83 | ContainerChaos.IntervalType intervalType, ContainerChaos.KillSignal killSignal) { 84 | configurePumbaCube(PumbaChaosCommandBuilder.create() 85 | .containers(containersType) 86 | .interval(intervalType) 87 | .chaosOperation(ChaosOperation.KILL) 88 | .killSignal(killSignal) 89 | , true); 90 | return this; 91 | } 92 | 93 | @Override 94 | public void exec() throws Exception { 95 | startPumba(); 96 | } 97 | 98 | @Override 99 | public void exec(Perform perform) throws Exception { 100 | try { 101 | startPumba(); 102 | perform.execute(); 103 | } finally { 104 | stopPumba(); 105 | } 106 | } 107 | 108 | @Override 109 | public void exec(RunCondition runCondition, Perform perform) throws Exception { 110 | try { 111 | startPumba(); 112 | while (runCondition.isExecutable()) { 113 | perform.execute(); 114 | } 115 | } finally { 116 | stopPumba(); 117 | } 118 | } 119 | 120 | private void startPumba() { 121 | cubeController.create(StandaloneContainer.Builder.DEFAULT_NAME); 122 | cubeController.start(StandaloneContainer.Builder.DEFAULT_NAME); 123 | } 124 | 125 | private void stopPumba() { 126 | cubeController.stop(StandaloneContainer.Builder.DEFAULT_NAME); 127 | cubeController.destroy(StandaloneContainer.Builder.DEFAULT_NAME); 128 | } 129 | 130 | private void configurePumbaCube(PumbaChaosCommandBuilder chaosCommand, boolean random) { 131 | final Cube cube = cubeRegistry.getCube(StandaloneContainer.Builder.DEFAULT_NAME); 132 | 133 | DockerCube dockerCube = (DockerCube) cube; 134 | dockerCube.configuration() 135 | .setCmd(PumbaCommandLineCreator.run(chaosCommand.build(), random, cubeDockerConfiguration)); 136 | } 137 | 138 | enum ChaosOperation 139 | 140 | { 141 | STOP, RM, KILL 142 | } 143 | 144 | public static class PumbaChaosCommandBuilder { 145 | 146 | private static final String SEPARATOR = "|"; 147 | public static final String SECONDS = "s"; 148 | 149 | private ContainerChaos.ContainersType containersType; 150 | private ContainerChaos.IntervalType intervalType; 151 | private ChaosOperation chaosOperation; 152 | private ContainerChaos.KillSignal killSignal; 153 | 154 | private PumbaChaosCommandBuilder() { 155 | } 156 | 157 | public static PumbaChaosCommandBuilder create() { 158 | return new PumbaChaosCommandBuilder(); 159 | } 160 | 161 | public PumbaChaosCommandBuilder containers(ContainerChaos.ContainersType containersType) { 162 | this.containersType = containersType; 163 | return this; 164 | } 165 | 166 | public PumbaChaosCommandBuilder interval(ContainerChaos.IntervalType intervalType) { 167 | this.intervalType = intervalType; 168 | return this; 169 | } 170 | 171 | public PumbaChaosCommandBuilder chaosOperation(ChaosOperation chaosOperation) { 172 | this.chaosOperation = chaosOperation; 173 | return this; 174 | } 175 | 176 | public PumbaChaosCommandBuilder killSignal(ContainerChaos.KillSignal killSignal) { 177 | this.killSignal = killSignal; 178 | return this; 179 | } 180 | 181 | public String build() { 182 | StringBuilder command = new StringBuilder(); 183 | //containersType.getValue() + "|" + intervalType.getValue() + "s|KILL:" + killSignal.name() 184 | command.append(containersType.getValue()).append(SEPARATOR); 185 | command.append(intervalType.getValue()).append(SECONDS).append(SEPARATOR); 186 | command.append(chaosOperation.name()); 187 | 188 | if (this.chaosOperation == ChaosOperation.KILL) { 189 | command.append(":").append(killSignal.name()); 190 | } 191 | return command.toString(); 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # traverses directory structure from process work directory to filesystem root 188 | # first directory with .mvn subdirectory is considered project base directory 189 | find_maven_basedir() { 190 | local basedir=$(pwd) 191 | local wdir=$(pwd) 192 | while [ "$wdir" != '/' ] ; do 193 | if [ -d "$wdir"/.mvn ] ; then 194 | basedir=$wdir 195 | break 196 | fi 197 | wdir=$(cd "$wdir/.."; pwd) 198 | done 199 | echo "${basedir}" 200 | } 201 | 202 | # concatenates all lines of a file 203 | concat_lines() { 204 | if [ -f "$1" ]; then 205 | echo "$(tr -s '\n' ' ' < "$1")" 206 | fi 207 | } 208 | 209 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 210 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 211 | 212 | # For Cygwin, switch paths to Windows format before running java 213 | if $cygwin; then 214 | [ -n "$M2_HOME" ] && 215 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 216 | [ -n "$JAVA_HOME" ] && 217 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 218 | [ -n "$CLASSPATH" ] && 219 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 220 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 221 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 222 | fi 223 | 224 | # Provide a "standardized" way to retrieve the CLI args that will 225 | # work with both Windows and non-Windows executions. 226 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 227 | export MAVEN_CMD_LINE_ARGS 228 | 229 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 230 | 231 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ 232 | exec "$JAVACMD" \ 233 | $MAVEN_OPTS \ 234 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 235 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 236 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 237 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/ToxicProxyHandler.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.docker.impl.client.config.CubeContainer; 4 | import org.arquillian.cube.docker.impl.client.config.DockerCompositions; 5 | import org.arquillian.cube.docker.impl.client.config.ExposedPort; 6 | import org.arquillian.cube.docker.impl.client.config.Link; 7 | import org.arquillian.cube.docker.impl.client.config.PortBinding; 8 | import org.arquillian.cube.q.spi.NetworkChaosConfiguration; 9 | import org.arquillian.cube.q.spi.Proxy; 10 | import org.arquillian.cube.q.spi.Proxy.Relation; 11 | import org.arquillian.cube.q.spi.ProxyManager; 12 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 13 | import org.arquillian.cube.q.toxic.client.ToxiProxyScenario; 14 | import org.arquillian.cube.spi.Cube; 15 | import org.arquillian.cube.spi.metadata.HasPortBindings; 16 | import org.arquillian.cube.spi.metadata.HasPortBindings.PortAddress; 17 | import org.jboss.arquillian.core.api.Injector; 18 | import org.jboss.arquillian.core.api.Instance; 19 | import org.jboss.arquillian.core.api.InstanceProducer; 20 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 21 | import org.jboss.arquillian.core.api.annotation.Inject; 22 | 23 | import java.util.ArrayList; 24 | import java.util.Collection; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | public class ToxicProxyHandler implements ProxyManager { 29 | 30 | @Inject 31 | private Instance proxyInst; 32 | 33 | @Inject 34 | Instance networkChaosConfigurationInstance; 35 | 36 | @Inject 37 | private Instance injectorInstance; 38 | 39 | @Inject 40 | @ApplicationScoped 41 | private InstanceProducer scenarioInst; 42 | 43 | @Override 44 | public Proxy install(DockerCompositions cubes) { 45 | Proxy.Builder builder = Proxy.create(); 46 | 47 | final Map containers = cubes.getContainers(); 48 | for (Map.Entry cube : containers.entrySet()) { 49 | String cubeName = cube.getKey(); 50 | 51 | CubeContainer data = cube.getValue(); 52 | 53 | NetworkChaosConfiguration networkChaosConfiguration = networkChaosConfigurationInstance.get(); 54 | if (!networkChaosConfiguration.isToxifyPortBinding()) { 55 | // Now we need to expose the same ports of the linked service 56 | final Collection links = data.getLinks(); 57 | 58 | if (links != null) { 59 | for (Link link : links) { 60 | if (containers.containsKey(link.getName())) { 61 | final CubeContainer linkedContainer = containers.get(link.getName()); 62 | 63 | final Collection portBindings = linkedContainer.getPortBindings(); 64 | if (portBindings != null) { 65 | for (PortBinding portBinding : portBindings) { 66 | final ExposedPort exposedPort = portBinding.getExposedPort(); 67 | if (exposedPort != null) { 68 | builder.containerExpose(link.getName(), exposedPort.getExposed(), 69 | exposedPort.getType()); 70 | } 71 | } 72 | } 73 | 74 | final Collection exposedPorts = linkedContainer.getExposedPorts(); 75 | if (exposedPorts != null) { 76 | for (ExposedPort exposedPort : exposedPorts) { 77 | builder.containerExpose(link.getName(), exposedPort.getExposed(), 78 | exposedPort.getType()); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | // redirect links to proxy and adds links to proxy to the old links 85 | data.setLinks(updateLinks(builder, data.getLinks())); 86 | } else { 87 | 88 | // finally we need to detect all cubes that binds a port to the host computer 89 | 90 | // all services that binds a port to host computer should not binds anymore (since they will be bound by toxiproxy) 91 | // so we need to remove all of them 92 | 93 | List removedBoundPorts = new ArrayList<>(); 94 | if (data.getPortBindings() != null) { 95 | Collection ports = data.getPortBindings(); 96 | for (PortBinding binding : ports) { 97 | builder.containerBinds(cubeName, binding.getBound(), binding.getExposedPort().getExposed(), 98 | binding.getExposedPort().getType()); 99 | removedBoundPorts.add(binding); 100 | } 101 | data.setPortBindings(null); 102 | } 103 | 104 | // if it has exposed ports, these ports should be added as well 105 | if (data.getExposedPorts() != null) { 106 | Collection ports = data.getExposedPorts(); 107 | for (ExposedPort port : ports) { 108 | builder.containerExpose(cubeName, port.getExposed(), port.getType()); 109 | } 110 | } 111 | 112 | // if 113 | if (removedBoundPorts.size() > 0) { 114 | Collection ports = new ArrayList<>(); 115 | if (data.getExposedPorts() != null) { 116 | ports = data.getExposedPorts(); 117 | } 118 | for (PortBinding binding : removedBoundPorts) { 119 | ports.add(binding.getExposedPort()); 120 | } 121 | data.setExposedPorts(ports); 122 | } 123 | } 124 | } 125 | 126 | return builder.build(); 127 | } 128 | 129 | private Collection updateLinks(Proxy.Builder proxy, Collection links) { 130 | final Collection updatedLinks = new ArrayList<>(); 131 | if (links != null) { 132 | for (Link link : links) { 133 | proxy.containerLinks(link.getName(), link.getName() + "_toxiproxy"); 134 | updatedLinks.add(new Link(proxy.getName(), link.getAlias())); 135 | } 136 | updatedLinks.add(new Link(proxy.getName(), proxy.getName())); 137 | } 138 | return updatedLinks; 139 | } 140 | 141 | @Override 142 | public void proxyStarted(Cube cube) { 143 | Proxy proxy = proxyInst.get(); 144 | 145 | if (!cube.hasMetadata(HasPortBindings.class)) { 146 | throw new IllegalStateException("Proxy Cube " + proxy.getName() + " has no PortBinding data."); 147 | } 148 | HasPortBindings bindings = cube.getMetadata(HasPortBindings.class); 149 | 150 | PortAddress communicationPort = bindings.getMappedAddress(proxy.getCommunicationPort().getExposed()); 151 | 152 | scenarioInst.set( 153 | new ToxiProxyScenario(ToxiProxyClient.Builder.create(communicationPort.getIP(), communicationPort.getPort()), 154 | injectorInstance.get())); 155 | } 156 | 157 | @Override 158 | public void populateProxies() { 159 | Proxy proxy = proxyInst.get(); 160 | ToxiProxyScenario scenario = scenarioInst.get(); 161 | if (scenario == null) { 162 | throw new IllegalStateException("Scenario is not Initiated. Proxy not started before others."); 163 | } 164 | 165 | for (Relation rel : proxy.getRelations()) { 166 | String relName = rel.getFrom() + ":" + rel.getPort().getExposed(); 167 | String localExp = "0.0.0.0:" + rel.getPort().getExposed(); 168 | String remoteExp = rel.getTo() + ":" + rel.getPort().getExposed(); 169 | 170 | System.out.println("Registered proxy " + relName + " " + localExp + " -> " + remoteExp); 171 | scenario.register( 172 | relName, 173 | localExp, 174 | remoteExp); 175 | } 176 | } 177 | 178 | @Override 179 | public void cubeStopped(Cube cube) { 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /toxic/src/main/java/org/arquillian/cube/q/toxic/QNetworkChaosToxic.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.toxic; 2 | 3 | import org.arquillian.cube.q.api.NetworkChaos; 4 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 5 | import org.arquillian.cube.q.toxic.client.ToxiProxyScenario; 6 | import org.jboss.arquillian.core.api.InstanceProducer; 7 | import org.jboss.arquillian.core.api.annotation.ApplicationScoped; 8 | import org.jboss.arquillian.core.api.annotation.Inject; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class QNetworkChaosToxic implements NetworkChaos { 15 | 16 | private ToxiProxyScenario scenario; 17 | 18 | @Inject 19 | @ApplicationScoped 20 | private InstanceProducer toxicActionInstanceProducer; 21 | 22 | public QNetworkChaosToxic(ToxiProxyScenario scenario) { 23 | this.scenario = scenario; 24 | } 25 | 26 | @Override 27 | public Action on(String machine, int port) { 28 | ToxicAction toxicAction = new ToxicAction(machine + ":" + port); 29 | toxicActionInstanceProducer.set(toxicAction); 30 | return toxicActionInstanceProducer.get(); 31 | } 32 | 33 | public class ToxicAction implements Action { 34 | 35 | private List toxics = new ArrayList<>(); 36 | 37 | private String name; 38 | 39 | public ToxicAction(String name) { 40 | this.name = name; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | @Override 48 | public Action down() { 49 | final ToxiProxyClient.Down down = 50 | new ToxiProxyClient.Down("down", ToxicDirectionStream.DOWNSTREAM.name(), 1f); 51 | toxics.add(down); 52 | return this; 53 | } 54 | 55 | @Override 56 | public Action timeout(TimeoutType timeType) { 57 | final ToxiProxyClient.Timeout toxic = 58 | new ToxiProxyClient.Timeout("timeout_downstream", ToxicDirectionStream.DOWNSTREAM.name(), 1f, timeType); 59 | toxics.add(toxic); 60 | return this; 61 | } 62 | 63 | @Override 64 | public Action timeout(TimeoutType timeType, ToxicityType toxicityType, 65 | ToxicDirectionStream toxicDirectionStream) { 66 | final ToxiProxyClient.Timeout toxic = 67 | new ToxiProxyClient.Timeout("timeout_" + toxicDirectionStream.name().toLowerCase(), 68 | toxicDirectionStream.name(), toxicityType.getValue(), timeType); 69 | toxics.add(toxic); 70 | 71 | return this; 72 | } 73 | 74 | @Override 75 | public Action latency(LatencyType latencyType) { 76 | final ToxiProxyClient.Latency toxic = 77 | new ToxiProxyClient.Latency("latency_downstream", ToxicDirectionStream.DOWNSTREAM.name(), 1f, latencyType, 78 | JitterType.noJitter()); 79 | toxics.add(toxic); 80 | 81 | return this; 82 | } 83 | 84 | @Override 85 | public Action latency(LatencyType latencyType, JitterType jitterType, ToxicityType toxicityType, 86 | ToxicDirectionStream toxicDirectionStream) { 87 | final ToxiProxyClient.Latency toxic = 88 | new ToxiProxyClient.Latency("latency_" + toxicDirectionStream.name().toLowerCase(), 89 | toxicDirectionStream.name(), toxicityType.getValue(), latencyType, jitterType); 90 | toxics.add(toxic); 91 | 92 | return this; 93 | } 94 | 95 | @Override 96 | public Action bandwidth(RateType rateType) { 97 | final ToxiProxyClient.Bandwidth toxic = 98 | new ToxiProxyClient.Bandwidth("bandwidth_downstream", ToxicDirectionStream.DOWNSTREAM.name(), 1f, 99 | rateType); 100 | toxics.add(toxic); 101 | 102 | return this; 103 | } 104 | 105 | @Override 106 | public Action bandwidth(RateType rateType, ToxicityType toxicityType, ToxicDirectionStream toxicDirectionStream) { 107 | final ToxiProxyClient.Bandwidth toxic = 108 | new ToxiProxyClient.Bandwidth("bandwidth_" + toxicDirectionStream.name().toLowerCase(), 109 | toxicDirectionStream.name(), toxicityType.getValue(), rateType); 110 | toxics.add(toxic); 111 | 112 | return this; 113 | } 114 | 115 | @Override 116 | public Action slowClose(DelayType delayType) { 117 | final ToxiProxyClient.SlowClose toxic = 118 | new ToxiProxyClient.SlowClose("slowclose_downstream", ToxicDirectionStream.DOWNSTREAM.name(), 1f, 119 | delayType); 120 | toxics.add(toxic); 121 | 122 | return this; 123 | } 124 | 125 | @Override 126 | public Action slowClose(DelayType delayType, ToxicityType toxicityType, 127 | ToxicDirectionStream toxicDirectionStream) { 128 | final ToxiProxyClient.SlowClose toxic = 129 | new ToxiProxyClient.SlowClose("slowclose_" + toxicDirectionStream.name().toLowerCase(), 130 | toxicDirectionStream.name(), toxicityType.getValue(), delayType); 131 | toxics.add(toxic); 132 | 133 | return this; 134 | } 135 | 136 | @Override 137 | public Action slice(SliceAverageSizeType sliceAverageSizeType, DelayType delayType) { 138 | final ToxiProxyClient.Slice toxic = 139 | new ToxiProxyClient.Slice("slice_downstream", ToxicDirectionStream.DOWNSTREAM.name(), 1f, 140 | sliceAverageSizeType.getValue(), delayType, 0); 141 | toxics.add(toxic); 142 | 143 | return this; 144 | } 145 | 146 | @Override 147 | public Action slice(SliceAverageSizeType sliceAverageSizeType, DelayType delayType, 148 | SliceSizeVariationType sliceSizeVariationType, ToxicityType toxicityType, 149 | ToxicDirectionStream toxicDirectionStream) { 150 | final ToxiProxyClient.Slice toxic = 151 | new ToxiProxyClient.Slice("slice_" + toxicDirectionStream.name().toLowerCase(), 152 | toxicDirectionStream.name(), toxicityType.getValue(), sliceAverageSizeType.getValue(), delayType, 153 | sliceSizeVariationType.getValue()); 154 | toxics.add(toxic); 155 | 156 | return this; 157 | } 158 | 159 | @Override 160 | public void exec() throws Exception { 161 | run(); 162 | } 163 | 164 | @Override 165 | public void exec(Perform perform) throws Exception { 166 | run(perform); 167 | } 168 | 169 | @Override 170 | public void exec(RunCondition runCondition, Perform perform) throws Exception { 171 | run(runCondition, perform); 172 | } 173 | 174 | private void run() throws Exception { 175 | scenario.given(name) 176 | .using(toxics) 177 | .execute(); 178 | } 179 | 180 | private void run(final RunCondition runCondition, final Perform perform) throws Exception { 181 | 182 | final List toxicsWithDistributedValues = 183 | filterToxicsContainingDistributedValues(toxics); 184 | 185 | scenario.given(name) 186 | .using(toxics) 187 | .then(runCondition, () -> { 188 | while (runCondition.isExecutable()) { 189 | perform.execute(); 190 | 191 | //update distributed values for next round 192 | scenario.given(name) 193 | .using(toxicsWithDistributedValues) 194 | .update(); 195 | } 196 | }); 197 | } 198 | 199 | private void run(final Perform perform) throws Exception { 200 | scenario.given(name) 201 | .using(toxics) 202 | .then(perform::execute); 203 | } 204 | 205 | private List filterToxicsContainingDistributedValues( 206 | List toxics) { 207 | final List toxicsWithDistributedValues = new ArrayList<>(); 208 | 209 | for (ToxiProxyClient.BaseToxic toxic : toxics) { 210 | if (toxic.hasAnyDistributedField()) { 211 | toxicsWithDistributedValues.add(toxic); 212 | } 213 | } 214 | 215 | return Collections.unmodifiableList(toxicsWithDistributedValues); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | org.jboss 8 | jboss-parent 9 | 35 10 | 11 | 12 | 13 | 14 | 4.0.0 15 | 16 | 17 | org.arquillian.cube.q 18 | arquillian-cube-q 19 | 1.0.0.Final-SNAPSHOT 20 | 21 | pom 22 | 23 | Arquillian Cube Q Extension 24 | http://arquillian.org 25 | Arquillian Cube Q Extension to run controlled havoc in your environment 26 | 27 | 28 | 29 | arquillian.org 30 | Arquillian Community 31 | arquillian.org 32 | http://arquillian.org 33 | 34 | 35 | 36 | 37 | 38 | Apache License, Version 2.0 39 | http://www.apache.org/licenses/LICENSE-2.0.txt 40 | 41 | 42 | 43 | 44 | scm:git:git://git@github.com:arquillian/arquillian-cube-q.git 45 | scm:git:ssh://github.com/arquillian/arquillian-cube-q.git 46 | git://github.com/arquillian/arquillian-cube-q.git 47 | HEAD 48 | 49 | 50 | 51 | GitHub 52 | https://github.com/arquillian/arquillian-cube-q/issues 53 | 54 | 55 | 56 | 1.4.1.Final 57 | 1.3.0 58 | 2.1.3 59 | 4.12 60 | 2.25.1 61 | 3.12.1 62 | 0.0.3 63 | 2.4.0 64 | 65 | 66 | 67 | 68 | 69 | org.arquillian.cube.q 70 | arquillian-cube-q-api 71 | ${project.version} 72 | 73 | 74 | org.arquillian.cube.q 75 | arquillian-cube-q-spi 76 | ${project.version} 77 | 78 | 79 | org.arquillian.cube.q 80 | arquillian-cube-q-core 81 | ${project.version} 82 | 83 | 84 | org.arquillian.cube.q 85 | arquillian-cube-q-toxic 86 | ${project.version} 87 | 88 | 89 | eu.rekawek.toxiproxy 90 | toxiproxy-java 91 | ${version.toxiproxyjava} 92 | 93 | 94 | org.arquillian.cube.q 95 | arquillian-cube-q-pumba 96 | ${project.version} 97 | 98 | 99 | org.arquillian.cube.q 100 | arquillian-cube-q-simianarmy 101 | ${project.version} 102 | 103 | 104 | org.arquillian.cube 105 | arquillian-cube-api 106 | ${version.arquillian_cube} 107 | 108 | 109 | org.arquillian.cube 110 | arquillian-cube-spi 111 | ${version.arquillian_cube} 112 | 113 | 114 | org.arquillian.cube 115 | arquillian-cube-core 116 | ${version.arquillian_cube} 117 | 118 | 119 | org.arquillian.cube 120 | arquillian-cube-docker 121 | ${version.arquillian_cube} 122 | 123 | 124 | org.arquillian.cube 125 | arquillian-cube-requirement 126 | ${version.arquillian_cube} 127 | 128 | 129 | org.jboss.arquillian.core 130 | arquillian-core-impl-base 131 | ${version.arquillian_core} 132 | 133 | 134 | org.jboss.arquillian.config 135 | arquillian-config-api 136 | ${version.arquillian_core} 137 | 138 | 139 | org.arquillian.reporter 140 | arquillian-core-reporter-api 141 | ${version.arquillian.reporter} 142 | 143 | 144 | org.arquillian.reporter 145 | arquillian-reporter-impl 146 | ${version.arquillian.reporter} 147 | 148 | 149 | org.jboss.arquillian.core 150 | arquillian-core-impl-base 151 | ${version.arquillian_core} 152 | tests 153 | test 154 | 155 | 156 | org.arquillian.cube.q 157 | arquillian-cube-q-reporter 158 | ${project.version} 159 | test 160 | 161 | 162 | org.arquillian.reporter 163 | arquillian-reporter-impl 164 | ${version.arquillian.reporter} 165 | tests 166 | test 167 | 168 | 169 | org.arquillian.reporter 170 | arquillian-reporter-depchain 171 | ${version.arquillian.reporter} 172 | pom 173 | 174 | 175 | org.mockito 176 | mockito-core 177 | ${version.mockito} 178 | test 179 | 180 | 181 | junit 182 | junit 183 | ${version.junit} 184 | test 185 | 186 | 187 | org.assertj 188 | assertj-core 189 | ${version.assertj} 190 | test 191 | 192 | 193 | com.jayway.jsonpath 194 | json-path-assert 195 | ${version.json.path.assert} 196 | test 197 | 198 | 199 | 200 | 201 | 202 | api 203 | spi 204 | core 205 | 206 | toxic 207 | ftest-toxic 208 | ftest-toxic-frontend 209 | pumba 210 | ftest-pumba 211 | simianarmy 212 | ftest-simianarmy 213 | reporter 214 | ftest-toxic-reporter 215 | 216 | 217 | 218 | 219 | maven-release-plugin 220 | 221 | true 222 | false 223 | true 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /reporter/src/test/java/org/arquillian/cube/q/recorder/TakeNetworkChaosInformationTest.java: -------------------------------------------------------------------------------- 1 | package org.arquillian.cube.q.recorder; 2 | 3 | import org.arquillian.cube.q.toxic.QNetworkChaosToxic; 4 | import org.arquillian.cube.q.toxic.client.ToxiProxyClient; 5 | import org.arquillian.cube.q.toxic.event.ToxicCreated; 6 | import org.arquillian.cube.q.toxic.event.ToxicUpdated; 7 | import org.arquillian.reporter.api.builder.BuilderLoader; 8 | import org.arquillian.reporter.api.event.SectionEvent; 9 | import org.arquillian.reporter.api.model.entry.FileEntry; 10 | import org.arquillian.reporter.api.model.entry.KeyValueEntry; 11 | import org.arquillian.reporter.api.model.report.Report; 12 | import org.arquillian.reporter.api.model.report.TestMethodReport; 13 | import org.arquillian.reporter.config.ReporterConfiguration; 14 | import org.hamcrest.CoreMatchers; 15 | import org.jboss.arquillian.core.api.Event; 16 | import org.jboss.arquillian.test.spi.event.suite.After; 17 | import org.junit.Assert; 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.mockito.ArgumentCaptor; 22 | import org.mockito.Captor; 23 | import org.mockito.Mock; 24 | import org.mockito.runners.MockitoJUnitRunner; 25 | 26 | import java.io.File; 27 | import java.io.IOException; 28 | import java.lang.reflect.Field; 29 | import java.lang.reflect.Method; 30 | import java.util.ArrayList; 31 | import java.util.LinkedHashMap; 32 | import java.util.Map; 33 | 34 | import static com.jayway.jsonassert.impl.matcher.IsCollectionWithSize.hasSize; 35 | import static com.jayway.jsonpath.matchers.JsonPathMatchers.isJson; 36 | import static com.jayway.jsonpath.matchers.JsonPathMatchers.withJsonPath; 37 | import static org.arquillian.cube.q.api.NetworkChaos.RateType.rate; 38 | import static org.arquillian.cube.q.api.NetworkChaos.TimeoutType.timeoutInMillis; 39 | import static org.arquillian.cube.q.recorder.NetworkChaosInformationReportKey.TOXICITY_DETAILS_PATH; 40 | import static org.arquillian.reporter.impl.asserts.ReportAssert.assertThatReport; 41 | import static org.arquillian.reporter.impl.asserts.SectionAssert.assertThatSection; 42 | import static org.assertj.core.api.Assertions.assertThat; 43 | import static org.assertj.core.api.Assertions.entry; 44 | import static org.hamcrest.CoreMatchers.equalTo; 45 | import static org.mockito.Mockito.verify; 46 | import static org.mockito.Mockito.when; 47 | 48 | @RunWith(MockitoJUnitRunner.class) 49 | public class TakeNetworkChaosInformationTest { 50 | 51 | @Mock 52 | private QNetworkChaosToxic.ToxicAction toxicAction; 53 | 54 | @Mock 55 | private Event sectionEvent; 56 | 57 | @Captor 58 | private ArgumentCaptor sectionEventArgumentCaptor; 59 | 60 | @Before 61 | public void configureToxicAction() { 62 | when(toxicAction.getName()).thenReturn("helloworld"); 63 | BuilderLoader.load(); 64 | } 65 | 66 | @Test 67 | public void should_capture_toxic_details_after_create() throws NoSuchFieldException, IllegalAccessException { 68 | 69 | //given 70 | ToxiProxyClient.Timeout timeout = 71 | new ToxiProxyClient.Timeout("timeout_downstream", "DOWNSTREAM", 1.0f, timeoutInMillis(1000)); 72 | ToxicCreated toxicCreated = new ToxicCreated(timeout); 73 | 74 | TakeNetworkChaosInformation takeNetworkChaosInformation = new TakeNetworkChaosInformation(); 75 | ArrayList> toxics = getToxicList(takeNetworkChaosInformation); 76 | 77 | //when 78 | takeNetworkChaosInformation.captureToxicDetailsAfterCreate(toxicCreated, toxicAction); 79 | 80 | //then 81 | assertThat(toxics).hasSize(1); 82 | assertThat(toxics.get(0)).containsExactly(entry("actionon", "helloworld"), 83 | entry("type", "Timeout"), 84 | entry("phase", "create"), 85 | entry("toxic", timeout) 86 | ); 87 | } 88 | 89 | @Test 90 | public void should_capture_toxic_details_after_update() throws NoSuchFieldException, IllegalAccessException { 91 | //given 92 | ToxiProxyClient.Bandwidth bandwidth = 93 | new ToxiProxyClient.Bandwidth("bandwidth_downstream", "DOWNSTREAM", 1.0f, rate(1000)); 94 | ToxicUpdated toxicUpdated = new ToxicUpdated(bandwidth); 95 | 96 | TakeNetworkChaosInformation takeNetworkChaosInformation = new TakeNetworkChaosInformation(); 97 | ArrayList> toxics = getToxicList(takeNetworkChaosInformation); 98 | 99 | //when 100 | takeNetworkChaosInformation.captureToxicDetailsAfterUpdate(toxicUpdated, toxicAction); 101 | 102 | //then 103 | assertThat(toxics).hasSize(1); 104 | assertThat(toxics.get(0)).containsExactly( 105 | entry("actionon", "helloworld"), 106 | entry("type", "Bandwidth"), 107 | entry("phase", "update"), 108 | entry("toxic", bandwidth) 109 | ); 110 | } 111 | 112 | @Test 113 | public void should_create_json_for_toxic_creation_and_write_it_to_file() 114 | throws IOException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException { 115 | //given 116 | ToxiProxyClient.Timeout timeout = 117 | new ToxiProxyClient.Timeout("timeout_downstream", "DOWNSTREAM", 1.0f, timeoutInMillis(1000)); 118 | ToxicCreated toxicCreated = new ToxicCreated(timeout); 119 | ReporterConfiguration reporterConfiguration = ReporterConfiguration.fromMap(new LinkedHashMap<>()); 120 | Method method = getMethod("should_create_json_for_toxic_updation_and_write_to_file"); 121 | String path = getFilePath(reporterConfiguration, method.getName()); 122 | 123 | TakeNetworkChaosInformation takeNetworkChaosInformation = new TakeNetworkChaosInformation(); 124 | takeNetworkChaosInformation.sectionEvent = sectionEvent; 125 | 126 | //when 127 | takeNetworkChaosInformation.captureToxicDetailsAfterCreate(toxicCreated, toxicAction); 128 | takeNetworkChaosInformation.reportToxicConfiguration(new After(TakeNetworkChaosInformationTest.class, method), 129 | reporterConfiguration); 130 | 131 | //then 132 | File json = new File(path); 133 | Assert.assertThat(json, isJson()); 134 | Assert.assertThat(json, isJson(CoreMatchers.allOf( 135 | withJsonPath("$.services", hasSize(1)), 136 | withJsonPath("$.services[0].actionon", equalTo("helloworld")), 137 | withJsonPath("$.services[0].type", equalTo("Timeout")), 138 | withJsonPath("$.services[0].phase", equalTo("create")), 139 | withJsonPath("$.services[0].toxic.name", equalTo("timeout_downstream")), 140 | withJsonPath("$.services[0].toxic.stream", equalTo("DOWNSTREAM")), 141 | withJsonPath("$.services[0].toxic.toxcicity", equalTo(1.0)), 142 | withJsonPath("$.services[0].toxic.timeout", equalTo(1000))))); 143 | } 144 | 145 | @Test 146 | public void should_create_json_for_toxic_updation_and_write_to_file() 147 | throws IOException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException { 148 | //given 149 | ToxiProxyClient.Timeout timeout = 150 | new ToxiProxyClient.Timeout("timeout_downstream", "DOWNSTREAM", 1.0f, timeoutInMillis(1000)); 151 | ToxicUpdated toxicUpdated = new ToxicUpdated(timeout); 152 | ReporterConfiguration reporterConfiguration = ReporterConfiguration.fromMap(new LinkedHashMap<>()); 153 | 154 | Method method = getMethod("should_create_json_for_toxic_updation_and_write_to_file"); 155 | String path = getFilePath(reporterConfiguration, method.getName()); 156 | 157 | TakeNetworkChaosInformation takeNetworkChaosInformation = new TakeNetworkChaosInformation(); 158 | takeNetworkChaosInformation.sectionEvent = sectionEvent; 159 | 160 | //when 161 | takeNetworkChaosInformation.captureToxicDetailsAfterUpdate(toxicUpdated, toxicAction); 162 | takeNetworkChaosInformation.reportToxicConfiguration(new After(TakeNetworkChaosInformationTest.class, 163 | method), 164 | reporterConfiguration); 165 | 166 | //then 167 | File json = new File(path); 168 | Assert.assertThat(json, isJson()); 169 | Assert.assertThat(json, isJson(CoreMatchers.allOf( 170 | withJsonPath("$.services", hasSize(1)), 171 | withJsonPath("$.services[0].actionon", equalTo("helloworld")), 172 | withJsonPath("$.services[0].type", equalTo("Timeout")), 173 | withJsonPath("$.services[0].phase", equalTo("update")), 174 | withJsonPath("$.services[0].toxic.name", equalTo("timeout_downstream")), 175 | withJsonPath("$.services[0].toxic.stream", equalTo("DOWNSTREAM")), 176 | withJsonPath("$.services[0].toxic.toxcicity", equalTo(1.0)), 177 | withJsonPath("$.services[0].toxic.timeout", equalTo(1000))))); 178 | } 179 | 180 | @Test 181 | public void should_report_json_file_with_toxicity_params_after_test() throws NoSuchMethodException, IOException { 182 | 183 | TakeNetworkChaosInformation takeNetworkChaosInformation = new TakeNetworkChaosInformation(); 184 | takeNetworkChaosInformation.sectionEvent = sectionEvent; 185 | 186 | ReporterConfiguration reporterConfiguration = ReporterConfiguration.fromMap(new LinkedHashMap<>()); 187 | final Method method = getMethod("should_report_json_file_with_toxicity_params_after_test"); 188 | takeNetworkChaosInformation.reportToxicConfiguration(new After(TakeNetworkChaosInformationTest.class, 189 | method), reporterConfiguration); 190 | 191 | verify(sectionEvent).fire(sectionEventArgumentCaptor.capture()); 192 | 193 | final SectionEvent sectionEvent = sectionEventArgumentCaptor.getValue(); 194 | final String methodName = method.getName(); 195 | 196 | assertThatSection(sectionEvent) 197 | .hasSectionId(methodName) 198 | .hasReportOfTypeThatIsAssignableFrom(TestMethodReport.class); 199 | 200 | final Report report = sectionEvent.getReport(); 201 | 202 | assertThatReport(report) 203 | .hasName(methodName) 204 | .hasNumberOfEntries(1) 205 | .hasEntriesContaining(new KeyValueEntry(TOXICITY_DETAILS_PATH, 206 | new FileEntry("reports/chaos/should_report_json_file_with_toxicity_params_after_test.json"))); 207 | } 208 | 209 | private Method getMethod(String name) throws NoSuchMethodException { 210 | return TakeNetworkChaosInformationTest.class.getMethod(name); 211 | } 212 | 213 | private ArrayList> getToxicList(TakeNetworkChaosInformation takeNetworkChaosInformation) 214 | throws NoSuchFieldException, IllegalAccessException { 215 | 216 | Field field = takeNetworkChaosInformation.getClass().getDeclaredField("toxics"); 217 | field.setAccessible(true); 218 | return (ArrayList>) field.get(takeNetworkChaosInformation); 219 | } 220 | 221 | private String getFilePath(ReporterConfiguration reporterConfiguration, String methodName) { 222 | String path = reporterConfiguration.getRootDirectory() + "/reports/chaos/" + methodName + ".json"; 223 | 224 | return path.replace("/", File.separator); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------