Skip to content

Commit

Permalink
Add possibility to run daemon in a docker container (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
javer authored Aug 3, 2021
1 parent 8f2926b commit 3f34326
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 129 deletions.
7 changes: 4 additions & 3 deletions Behat/Context/BaseSphinxContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Javer\SphinxBundle\Behat\Context;

use Behat\Behat\Context\Context;
use Javer\SphinxBundle\Sphinx\Daemon;
use Javer\SphinxBundle\Sphinx\Daemon\DaemonInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class BaseSphinxContext implements Context
Expand All @@ -26,8 +26,9 @@ public function createSearchIndexAndRunSphinx(): void

$configPath = $dataDir . '/sphinx.conf';

/** @var Daemon $daemon */
$daemon = $container->get('sphinx.daemon')->setConfigPath($configPath);
/** @var DaemonInterface $daemon */
$daemon = $container->get('sphinx.daemon');
$daemon->setConfigPath($configPath);
$daemon->stop();

$indexes = $container->get('sphinx.converter.mysql_to_realtime')
Expand Down
1 change: 1 addition & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('config_path')->defaultValue('%kernel.project_dir%/config/sphinx.conf')->end()
->scalarNode('data_dir')->defaultValue('%kernel.cache_dir%/sphinx')->end()
->scalarNode('searchd_path')->defaultValue('searchd')->end()
->scalarNode('docker_image')->defaultValue('')->end()
->end();

return $treeBuilder;
Expand Down
12 changes: 12 additions & 0 deletions DependencyInjection/JaverSphinxExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Javer\SphinxBundle\DependencyInjection;

use Javer\SphinxBundle\Sphinx\Daemon\DaemonInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
Expand All @@ -28,5 +30,15 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setParameter('javer_sphinx.config_path', $config['config_path']);
$container->setParameter('javer_sphinx.data_dir', $config['data_dir']);
$container->setParameter('javer_sphinx.searchd_path', $config['searchd_path']);
$container->setParameter('javer_sphinx.docker_image', $config['docker_image']);

if (is_string($config['docker_image']) && $config['docker_image'] !== '') {
$daemonDefinition = 'sphinx.daemon.docker';
} else {
$daemonDefinition = 'sphinx.daemon.native';
}

$container->setDefinition('sphinx.daemon', new ChildDefinition($daemonDefinition));
$container->setAlias(DaemonInterface::class, 'sphinx.daemon');
}
}
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,12 @@ It is not necessary when you declare fields for MySQL index in sphinx.conf, but
If you use sqlite as the database engine for running tests you should take into account that not all functions of the MySQL are presented in sqlite, so you should use portable analogs for these functions:
* `IF(condition, true, false)` -> `CASE WHEN condition THEN true ELSE false END`
* and so on

Docker
------

You can use a docker image to run the daemon in the docker container for the test environment, just add the following configuration option to `config/packages/test/javer_sphinx.yaml`:
```yaml
javer_sphinx:
docker_image: javer/sphinx
```
19 changes: 16 additions & 3 deletions Resources/config/services.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
parameters:
sphinx.manager.class: Javer\SphinxBundle\Sphinx\Manager
sphinx.daemon.class: Javer\SphinxBundle\Sphinx\Daemon
sphinx.daemon.native.class: Javer\SphinxBundle\Sphinx\Daemon\NativeDaemon
sphinx.daemon.docker.class: Javer\SphinxBundle\Sphinx\Daemon\DockerDaemon
sphinx.daemon.timeout.start: 1
sphinx.daemon.timeout.stop: 3
sphinx.event.subscriber.paginate.class: Javer\SphinxBundle\Event\Subscriber\PaginateSphinxQuerySubscriber
Expand Down Expand Up @@ -41,15 +42,27 @@ services:
tags:
- { name: data_collector, template: "@JaverSphinx/Collector/sphinx.html.twig", id: sphinx }

sphinx.daemon:
class: "%sphinx.daemon.class%"
sphinx.daemon.native:
abstract: true
class: "%sphinx.daemon.native.class%"
arguments:
- "%javer_sphinx.searchd_path%"
- "%javer_sphinx.config_path%"
- "%javer_sphinx.data_dir%/searchd.pid"
- "%sphinx.daemon.timeout.start%"
- "%sphinx.daemon.timeout.stop%"

sphinx.daemon.docker:
abstract: true
class: "%sphinx.daemon.docker.class%"
arguments:
- "%javer_sphinx.docker_image%"
- "%javer_sphinx.config_path%"
- "%javer_sphinx.data_dir%"
- "%javer_sphinx.data_dir%/searchd.cid"
- "%javer_sphinx.port%"
- "%sphinx.daemon.timeout.start%"

sphinx.converter.mysql_to_realtime:
class: "%sphinx.converter.mysql_to_realtime.class%"
arguments:
Expand Down
123 changes: 0 additions & 123 deletions Sphinx/Daemon.php

This file was deleted.

14 changes: 14 additions & 0 deletions Sphinx/Daemon/DaemonInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Javer\SphinxBundle\Sphinx\Daemon;

interface DaemonInterface
{
public function start(): void;

public function stop(): void;

public function isRunning(): bool;

public function setConfigPath(string $configPath): void;
}
94 changes: 94 additions & 0 deletions Sphinx/Daemon/DockerDaemon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Javer\SphinxBundle\Sphinx\Daemon;

use RuntimeException;

final class DockerDaemon implements DaemonInterface
{
public function __construct(
private string $dockerImage,
private string $configPath,
private string $dataDir,
private string $cidPath,
private int $port,
private int $startTimeout,
)
{
}

public function start(): void
{
$command = sprintf(
'docker run --rm --detach -v %s -v %s --cidfile %s --publish %d:%d %s searchd --nodetach -c %s',
escapeshellarg($this->dataDir . ':' . $this->dataDir),
escapeshellarg($this->configPath . ':' . $this->configPath),
escapeshellarg($this->cidPath),
$this->port,
$this->port,
escapeshellarg($this->dockerImage),
escapeshellarg($this->configPath)
);

exec($command, $output, $resultCode);

if ($resultCode !== 0) {
throw new RuntimeException(sprintf('Cannot start sphinx docker container, return code: %d', $resultCode));
}

sleep($this->startTimeout);
}

public function stop(): void
{
$containerId = $this->getContainerId();

if ($containerId === null) {
return;
}

unlink($this->cidPath);

$command = sprintf('docker stop %s', escapeshellarg($containerId));

exec($command, $output, $resultCode);
}

public function isRunning(): bool
{
$containerId = $this->getContainerId();

if ($containerId === null) {
return false;
}

$command = sprintf('docker ps -q --filter "id=%s"', $containerId);

exec($command, $output, $resultCode);

return $resultCode === 0
&& is_array($output)
&& count($output) === 1
&& str_starts_with($containerId, $output[0]);
}

public function setConfigPath(string $configPath): void
{
$this->configPath = $configPath;
}

private function getContainerId(): ?string
{
if ($this->cidPath === '' || !is_readable($this->cidPath)) {
return null;
}

$containerId = trim(file_get_contents($this->cidPath));

if (empty($containerId) || !preg_match('/^[a-z0-9]+$/i', $containerId)) {
return null;
}

return $containerId;
}
}
Loading

0 comments on commit 3f34326

Please sign in to comment.