diff --git a/README.md b/README.md
index 9548d8d..20acd78 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,10 @@ Chaos Monkey for Symfony applications. Try to attack your running Symfony App.
- Repository (not implemented)
- Service (not implemented)
+## Activators
+
+ - "Query param" - attack only if given query param is present (default `chaos`)
+
## Symfony
## How to use
@@ -62,10 +66,12 @@ chaos_monkey:
request:
enabled: true
priority: 0
+ activators:
+ query_param: false # if true then chaos monkey will be called only if given query param exist (with any value)
+ query_param_name: 'chaos'
```
## Roadmap
- - [ ] Query param activator
- [ ] Flex recipe
- [ ] Metrics (for example `chaos_monkey_request_count_assaulted`)
- [ ] Assault profiles - each profile can contain different assaults
diff --git a/src/Activator/QueryParamActivator.php b/src/Activator/QueryParamActivator.php
new file mode 100644
index 0000000..6a7c576
--- /dev/null
+++ b/src/Activator/QueryParamActivator.php
@@ -0,0 +1,23 @@
+enabled) {
+ return true;
+ }
+
+ return $request->query->has($this->paramName);
+ }
+}
diff --git a/src/DependencyInjection/ChaosMonkeyExtension.php b/src/DependencyInjection/ChaosMonkeyExtension.php
index 662462a..cd56f07 100644
--- a/src/DependencyInjection/ChaosMonkeyExtension.php
+++ b/src/DependencyInjection/ChaosMonkeyExtension.php
@@ -21,6 +21,10 @@
* },
* watchers: array{
* request: array{enabled: bool, priority: int}
+ * },
+ * activators: array{
+ * query_param: bool,
+ * query_param_name: string
* }
* }
*/
@@ -39,6 +43,7 @@ public function load(array $configs, ContainerBuilder $container): void
$config = $this->processConfiguration($configuration, $configs);
$this->setChaosMonkeySettings($container, $config);
+ $this->setActivators($container, $config);
$this->enableWatchers($container, $config);
}
@@ -77,4 +82,16 @@ private function enableWatchers(ContainerBuilder $container, array $config): voi
]);
}
}
+
+ /**
+ * @param ConfigArray $config
+ */
+ private function setActivators(ContainerBuilder $container, array $config): void
+ {
+ $queryParam = $container->getDefinition('chaos_monkey.activator.query_param');
+ $queryParam->setArguments([
+ '$enabled' => $config['activators']['query_param'],
+ '$paramName' => $config['activators']['query_param_name'],
+ ]);
+ }
}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index c940b5f..e19b23e 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -65,6 +65,13 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->end()
+ ->arrayNode('activators')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('query_param')->defaultFalse()->end()
+ ->scalarNode('query_param_name')->defaultValue('chaos')->end()
+ ->end()
+ ->end()
->end();
return $treeBuilder;
diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
index ebf6787..95f1375 100644
--- a/src/Resources/config/services.xml
+++ b/src/Resources/config/services.xml
@@ -14,6 +14,10 @@
+
+
+
+
diff --git a/src/Watcher/RequestWatcher.php b/src/Watcher/RequestWatcher.php
index 228521c..08597e0 100644
--- a/src/Watcher/RequestWatcher.php
+++ b/src/Watcher/RequestWatcher.php
@@ -5,15 +5,15 @@
namespace Chaos\Monkey\Symfony\Watcher;
use Chaos\Monkey\ChaosMonkey;
+use Chaos\Monkey\Symfony\Activator\QueryParamActivator;
use Symfony\Component\HttpKernel\Event\RequestEvent;
-class RequestWatcher
+final class RequestWatcher
{
- private ChaosMonkey $chaosMonkey;
-
- public function __construct(ChaosMonkey $chaosMonkey)
- {
- $this->chaosMonkey = $chaosMonkey;
+ public function __construct(
+ private readonly ChaosMonkey $chaosMonkey,
+ private readonly QueryParamActivator $queryParamActivator
+ ) {
}
public function onKernelRequest(RequestEvent $event): void
@@ -22,6 +22,8 @@ public function onKernelRequest(RequestEvent $event): void
return;
}
- $this->chaosMonkey->call();
+ if ($this->queryParamActivator->inChaos($event->getRequest())) {
+ $this->chaosMonkey->call();
+ }
}
}
diff --git a/tests/Activator/QueryParamActivatorTest.php b/tests/Activator/QueryParamActivatorTest.php
new file mode 100644
index 0000000..ae48104
--- /dev/null
+++ b/tests/Activator/QueryParamActivatorTest.php
@@ -0,0 +1,33 @@
+inChaos(new Request()));
+ }
+
+ public function testItIsNoInChaosIfEnabledAndParamMissing(): void
+ {
+ $activator = new QueryParamActivator(true);
+
+ self::assertFalse($activator->inChaos(new Request()));
+ }
+
+ public function testItIsInChaosIfEnabledAndParamExists(): void
+ {
+ $activator = new QueryParamActivator(true, 'bye');
+
+ self::assertTrue($activator->inChaos(new Request(['bye' => true])));
+ }
+}
diff --git a/tests/Controller/SymfonyControllerTest.php b/tests/Controller/SymfonyControllerTest.php
index d4fcdf8..c6760ec 100644
--- a/tests/Controller/SymfonyControllerTest.php
+++ b/tests/Controller/SymfonyControllerTest.php
@@ -5,9 +5,11 @@
namespace Chaos\Monkey\Symfony\Tests\Controller;
use Chaos\Monkey\Settings;
+use Chaos\Monkey\Symfony\Activator\QueryParamActivator;
use Chaos\Monkey\Symfony\Tests\Symfony\Kernel;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
+use Symfony\Component\HttpKernel\Exception\LockedHttpException;
use Symfony\Component\Stopwatch\Stopwatch;
class SymfonyControllerTest extends TestCase
@@ -49,7 +51,22 @@ public function testRequestExceptionAttack(): void
$this->enableExceptionAssault();
$this->client->request('GET', '/hello');
- self::assertEquals(500, $this->client->getResponse()->getStatusCode());
+ self::assertEquals(423, $this->client->getResponse()->getStatusCode());
+
+ $this->disableExceptionAssault();
+ }
+
+ public function testRequestExceptionAttackWithQueryParamActivatorEnabled(): void
+ {
+ $this->enableQueryParamActivator();
+ $this->enableExceptionAssault();
+ $this->client->request('GET', '/hello');
+
+ self::assertEquals(200, $this->client->getResponse()->getStatusCode());
+
+ $this->client->request('GET', '/hello?chaos=true');
+
+ self::assertEquals(423, $this->client->getResponse()->getStatusCode());
$this->disableExceptionAssault();
}
@@ -58,6 +75,7 @@ private function enableExceptionAssault(): void
{
$this->chaosMonkeySettings()->setEnabled(true);
$this->chaosMonkeySettings()->setExceptionActive(true);
+ $this->chaosMonkeySettings()->setExceptionClass(LockedHttpException::class);
$this->chaosMonkeySettings()->setProbability(100);
}
@@ -84,4 +102,9 @@ private function chaosMonkeySettings(): Settings
{
return $this->client->getContainer()->get('chaos_monkey')->settings();
}
+
+ private function enableQueryParamActivator(): void
+ {
+ $this->client->getContainer()->get('test.service_container')->set('chaos_monkey.activator.query_param', new QueryParamActivator(true));
+ }
}
diff --git a/tests/Symfony/Kernel.php b/tests/Symfony/Kernel.php
index c2ba8ed..7aade88 100644
--- a/tests/Symfony/Kernel.php
+++ b/tests/Symfony/Kernel.php
@@ -29,6 +29,7 @@ protected function configureContainer(ContainerConfigurator $container): void
{
$container->extension('framework', [
'secret' => 'S0ME_SECRET',
+ 'test' => true,
]);
$container->services()->set('logger', NullLogger::class);