parent
2d66390250
commit
c39c366f17
|
@ -51,6 +51,10 @@ COPY ./util/docker/mariadb /bd_build/mariadb/
|
||||||
RUN bash /bd_build/mariadb/setup.sh \
|
RUN bash /bd_build/mariadb/setup.sh \
|
||||||
&& rm -rf /bd_build/mariadb
|
&& rm -rf /bd_build/mariadb
|
||||||
|
|
||||||
|
COPY ./util/docker/redis /bd_build/redis/
|
||||||
|
RUN bash /bd_build/redis/setup.sh \
|
||||||
|
&& rm -rf /bd_build/redis
|
||||||
|
|
||||||
#
|
#
|
||||||
# START Operations as `azuracast` user
|
# START Operations as `azuracast` user
|
||||||
#
|
#
|
||||||
|
|
|
@ -74,7 +74,7 @@ class UptimeWait
|
||||||
|
|
||||||
$elapsed = 0;
|
$elapsed = 0;
|
||||||
while ($elapsed <= $this->timeout) {
|
while ($elapsed <= $this->timeout) {
|
||||||
if ($this->checkDatabase()) {
|
if ($this->checkRedis() && $this->checkDatabase()) {
|
||||||
$this->println('Services started up and ready!');
|
$this->println('Services started up and ready!');
|
||||||
die(0);
|
die(0);
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,54 @@ class UptimeWait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function checkRedis(): bool
|
||||||
|
{
|
||||||
|
$enableRedis = $this->envToBool($_ENV['ENABLE_REDIS'] ?? true);
|
||||||
|
$redisHost = $_ENV['REDIS_HOST'] ?? 'localhost';
|
||||||
|
$redisPort = (int)($_ENV['REDIS_PORT'] ?? 6379);
|
||||||
|
$redisDb = (int)($_ENV['REDIS_DB'] ?? 1);
|
||||||
|
|
||||||
|
$redisSocket = ('localhost' === $redisHost)
|
||||||
|
? '/run/redis/redis.sock'
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (!$enableRedis) {
|
||||||
|
$this->println('Redis disabled; skipping Redis check...');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$redis = new Redis();
|
||||||
|
if (null !== $redisSocket) {
|
||||||
|
$redis->connect($redisSocket);
|
||||||
|
} else {
|
||||||
|
$redis->connect($redisHost, $redisPort, 15);
|
||||||
|
}
|
||||||
|
$redis->select($redisDb);
|
||||||
|
|
||||||
|
$redis->ping();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($this->debugMode) {
|
||||||
|
$this->println($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function envToBool(string|bool $value): bool
|
||||||
|
{
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_starts_with(strtolower($value), 'y')
|
||||||
|
|| 'true' === strtolower($value)
|
||||||
|
|| '1' === $value;
|
||||||
|
}
|
||||||
|
|
||||||
protected function println(string $line): void
|
protected function println(string $line): void
|
||||||
{
|
{
|
||||||
echo $line . "\n";
|
echo $line . "\n";
|
||||||
|
|
|
@ -145,19 +145,41 @@ return [
|
||||||
App\Doctrine\ReloadableEntityManagerInterface::class => DI\Get(App\Doctrine\DecoratedEntityManager::class),
|
App\Doctrine\ReloadableEntityManagerInterface::class => DI\Get(App\Doctrine\DecoratedEntityManager::class),
|
||||||
Doctrine\ORM\EntityManagerInterface::class => DI\Get(App\Doctrine\DecoratedEntityManager::class),
|
Doctrine\ORM\EntityManagerInterface::class => DI\Get(App\Doctrine\DecoratedEntityManager::class),
|
||||||
|
|
||||||
|
// Redis cache
|
||||||
|
Redis::class => static function (Environment $environment) {
|
||||||
|
if (!$environment->enableRedis()) {
|
||||||
|
throw new App\Exception\BootstrapException('Redis is disabled on this installation.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $environment->getRedisSettings();
|
||||||
|
|
||||||
|
$redis = new Redis();
|
||||||
|
if (isset($settings['socket'])) {
|
||||||
|
$redis->connect($settings['socket']);
|
||||||
|
} else {
|
||||||
|
$redis->connect($settings['host'], $settings['port'], 15);
|
||||||
|
}
|
||||||
|
$redis->select($settings['db']);
|
||||||
|
|
||||||
|
return $redis;
|
||||||
|
},
|
||||||
|
|
||||||
Symfony\Contracts\Cache\CacheInterface::class => static function (
|
Symfony\Contracts\Cache\CacheInterface::class => static function (
|
||||||
Environment $environment,
|
Environment $environment,
|
||||||
Psr\Log\LoggerInterface $logger
|
Psr\Log\LoggerInterface $logger,
|
||||||
|
ContainerInterface $di
|
||||||
) {
|
) {
|
||||||
if ($environment->isTesting()) {
|
if ($environment->isTesting()) {
|
||||||
$cacheInterface = new Symfony\Component\Cache\Adapter\ArrayAdapter();
|
$cacheInterface = new Symfony\Component\Cache\Adapter\ArrayAdapter();
|
||||||
} else {
|
} elseif (!$environment->enableRedis()) {
|
||||||
$tempDir = $environment->getTempDirectory() . DIRECTORY_SEPARATOR . 'cache';
|
$tempDir = $environment->getTempDirectory() . DIRECTORY_SEPARATOR . 'cache';
|
||||||
$cacheInterface = new Symfony\Component\Cache\Adapter\FilesystemAdapter(
|
$cacheInterface = new Symfony\Component\Cache\Adapter\FilesystemAdapter(
|
||||||
'',
|
'',
|
||||||
0,
|
0,
|
||||||
$tempDir
|
$tempDir
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
$cacheInterface = new Symfony\Component\Cache\Adapter\RedisAdapter($di->get(Redis::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
$cacheInterface->setLogger($logger);
|
$cacheInterface->setLogger($logger);
|
||||||
|
@ -170,14 +192,24 @@ return [
|
||||||
Psr\Cache\CacheItemPoolInterface::class => DI\get(
|
Psr\Cache\CacheItemPoolInterface::class => DI\get(
|
||||||
Symfony\Contracts\Cache\CacheInterface::class
|
Symfony\Contracts\Cache\CacheInterface::class
|
||||||
),
|
),
|
||||||
Psr\SimpleCache\CacheInterface::class => static fn(
|
Psr\SimpleCache\CacheInterface::class => static function (Psr\Cache\CacheItemPoolInterface $cache) {
|
||||||
Psr\Cache\CacheItemPoolInterface $cache
|
return new Symfony\Component\Cache\Psr16Cache($cache);
|
||||||
) => new Symfony\Component\Cache\Psr16Cache($cache),
|
},
|
||||||
|
|
||||||
// Symfony Lock adapter
|
// Symfony Lock adapter
|
||||||
Symfony\Component\Lock\PersistingStoreInterface::class => static fn(
|
Symfony\Component\Lock\PersistingStoreInterface::class => static function (
|
||||||
|
ContainerInterface $di,
|
||||||
Environment $environment
|
Environment $environment
|
||||||
) => new Symfony\Component\Lock\Store\FlockStore($environment->getTempDirectory()),
|
) {
|
||||||
|
if ($environment->enableRedis()) {
|
||||||
|
$redis = $di->get(Redis::class);
|
||||||
|
$store = new Symfony\Component\Lock\Store\RedisStore($redis);
|
||||||
|
} else {
|
||||||
|
$store = new Symfony\Component\Lock\Store\FlockStore($environment->getTempDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $store;
|
||||||
|
},
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
App\Console\Application::class => static function (
|
App\Console\Application::class => static function (
|
||||||
|
@ -313,7 +345,7 @@ return [
|
||||||
|
|
||||||
Symfony\Component\Messenger\MessageBus::class => static function (
|
Symfony\Component\Messenger\MessageBus::class => static function (
|
||||||
App\MessageQueue\QueueManager $queueManager,
|
App\MessageQueue\QueueManager $queueManager,
|
||||||
App\LockFactory $lockFactory,
|
App\Lock\LockFactory $lockFactory,
|
||||||
Monolog\Logger $logger,
|
Monolog\Logger $logger,
|
||||||
ContainerInterface $di,
|
ContainerInterface $di,
|
||||||
App\Plugins $plugins,
|
App\Plugins $plugins,
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace App\Console\Command\Sync;
|
||||||
|
|
||||||
use App\Console\Command\CommandAbstract;
|
use App\Console\Command\CommandAbstract;
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
use Symfony\Component\Lock\Lock;
|
use Symfony\Component\Lock\Lock;
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace App\Console\Command\Sync;
|
||||||
|
|
||||||
use App\Entity\Repository\SettingsRepository;
|
use App\Entity\Repository\SettingsRepository;
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace App\Console\Command\Sync;
|
||||||
use App\Entity\Repository\SettingsRepository;
|
use App\Entity\Repository\SettingsRepository;
|
||||||
use App\Environment;
|
use App\Environment;
|
||||||
use App\Event\GetSyncTasks;
|
use App\Event\GetSyncTasks;
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use App\Sync\Task\AbstractTask;
|
use App\Sync\Task\AbstractTask;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
use Cron\CronExpression;
|
use Cron\CronExpression;
|
||||||
|
|
|
@ -201,10 +201,7 @@ class Settings implements Stringable
|
||||||
}
|
}
|
||||||
|
|
||||||
#[
|
#[
|
||||||
OA\Property(
|
OA\Property(description: "Whether to use high-performance static JSON for Now Playing data updates.", example: "false"),
|
||||||
description: "Whether to use high-performance static JSON for Now Playing data updates.",
|
|
||||||
example: "false"
|
|
||||||
),
|
|
||||||
ORM\Column,
|
ORM\Column,
|
||||||
Groups(self::GROUP_GENERAL)
|
Groups(self::GROUP_GENERAL)
|
||||||
]
|
]
|
||||||
|
|
|
@ -58,6 +58,11 @@ final class Environment
|
||||||
public const DB_USER = 'MYSQL_USER';
|
public const DB_USER = 'MYSQL_USER';
|
||||||
public const DB_PASSWORD = 'MYSQL_PASSWORD';
|
public const DB_PASSWORD = 'MYSQL_PASSWORD';
|
||||||
|
|
||||||
|
public const ENABLE_REDIS = 'ENABLE_REDIS';
|
||||||
|
public const REDIS_HOST = 'REDIS_HOST';
|
||||||
|
public const REDIS_PORT = 'REDIS_PORT';
|
||||||
|
public const REDIS_DB = 'REDIS_DB';
|
||||||
|
|
||||||
// Default settings
|
// Default settings
|
||||||
private array $defaults = [
|
private array $defaults = [
|
||||||
self::APP_NAME => 'AzuraCast',
|
self::APP_NAME => 'AzuraCast',
|
||||||
|
@ -71,6 +76,8 @@ final class Environment
|
||||||
self::AUTO_ASSIGN_PORT_MIN => 8000,
|
self::AUTO_ASSIGN_PORT_MIN => 8000,
|
||||||
self::AUTO_ASSIGN_PORT_MAX => 8499,
|
self::AUTO_ASSIGN_PORT_MAX => 8499,
|
||||||
|
|
||||||
|
self::ENABLE_REDIS => true,
|
||||||
|
|
||||||
self::SYNC_SHORT_EXECUTION_TIME => 600,
|
self::SYNC_SHORT_EXECUTION_TIME => 600,
|
||||||
self::SYNC_LONG_EXECUTION_TIME => 1800,
|
self::SYNC_LONG_EXECUTION_TIME => 1800,
|
||||||
|
|
||||||
|
@ -293,9 +300,27 @@ final class Environment
|
||||||
return $dbSettings;
|
return $dbSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function useLocalDatabase(): bool
|
public function enableRedis(): bool
|
||||||
{
|
{
|
||||||
return 'localhost' === ($this->data[self::DB_HOST] ?? 'localhost');
|
return self::envToBool($this->data[self::ENABLE_REDIS] ?? true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public function getRedisSettings(): array
|
||||||
|
{
|
||||||
|
$redisSettings = [
|
||||||
|
'host' => $this->data[self::REDIS_HOST] ?? 'localhost',
|
||||||
|
'port' => (int)($this->data[self::REDIS_PORT] ?? 6379),
|
||||||
|
'db' => (int)($this->data[self::REDIS_DB] ?? 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ('localhost' === $redisSettings['host'] && $this->isDocker()) {
|
||||||
|
$redisSettings['socket'] = '/run/redis/redis.sock';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redisSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isProfilingExtensionEnabled(): bool
|
public function isProfilingExtensionEnabled(): bool
|
||||||
|
|
|
@ -28,6 +28,7 @@ final class AzuraCastEnvFile extends AbstractEnvFile
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbSettings = $emptyEnv->getDatabaseSettings();
|
$dbSettings = $emptyEnv->getDatabaseSettings();
|
||||||
|
$redisSettings = $emptyEnv->getRedisSettings();
|
||||||
|
|
||||||
$config = [
|
$config = [
|
||||||
Environment::LANG => [
|
Environment::LANG => [
|
||||||
|
@ -151,6 +152,28 @@ final class AzuraCastEnvFile extends AbstractEnvFile
|
||||||
),
|
),
|
||||||
'default' => 100,
|
'default' => 100,
|
||||||
],
|
],
|
||||||
|
Environment::ENABLE_REDIS => [
|
||||||
|
'name' => __('Enable Redis'),
|
||||||
|
'description' => __(
|
||||||
|
'Disable to use a flatfile cache instead of Redis.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Environment::REDIS_HOST => [
|
||||||
|
'name' => __('Redis Host'),
|
||||||
|
'default' => $redisSettings['host'],
|
||||||
|
'required' => true,
|
||||||
|
],
|
||||||
|
Environment::REDIS_PORT => [
|
||||||
|
'name' => __('Redis Port'),
|
||||||
|
'default' => $redisSettings['port'],
|
||||||
|
'required' => true,
|
||||||
|
],
|
||||||
|
Environment::REDIS_DB => [
|
||||||
|
'name' => __('Redis Database Index'),
|
||||||
|
'options' => range(0, 15),
|
||||||
|
'default' => $redisSettings['db'],
|
||||||
|
'required' => true,
|
||||||
|
],
|
||||||
'PHP_MAX_FILE_SIZE' => [
|
'PHP_MAX_FILE_SIZE' => [
|
||||||
'name' => __('PHP Maximum POST File Size'),
|
'name' => __('PHP Maximum POST File Size'),
|
||||||
'default' => '25M',
|
'default' => '25M',
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App;
|
namespace App\Lock;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Lock\BlockingStoreInterface;
|
||||||
use Symfony\Component\Lock\LockFactory as SymfonyLockFactory;
|
use Symfony\Component\Lock\LockFactory as SymfonyLockFactory;
|
||||||
use Symfony\Component\Lock\LockInterface;
|
use Symfony\Component\Lock\LockInterface;
|
||||||
use Symfony\Component\Lock\PersistingStoreInterface;
|
use Symfony\Component\Lock\PersistingStoreInterface;
|
||||||
|
@ -16,6 +17,11 @@ final class LockFactory extends SymfonyLockFactory
|
||||||
PersistingStoreInterface $lockStore,
|
PersistingStoreInterface $lockStore,
|
||||||
LoggerInterface $logger
|
LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
|
if (!$lockStore instanceof BlockingStoreInterface) {
|
||||||
|
$lockStore = new RetryTillSaveStore($lockStore, 30, 1000);
|
||||||
|
$lockStore->setLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($lockStore);
|
parent::__construct($lockStore);
|
||||||
$this->setLogger($logger);
|
$this->setLogger($logger);
|
||||||
}
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Lock;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerAwareInterface;
|
||||||
|
use Psr\Log\LoggerAwareTrait;
|
||||||
|
use Psr\Log\NullLogger;
|
||||||
|
use Symfony\Component\Lock\BlockingStoreInterface;
|
||||||
|
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||||
|
use Symfony\Component\Lock\Key;
|
||||||
|
use Symfony\Component\Lock\PersistingStoreInterface;
|
||||||
|
|
||||||
|
use const PHP_INT_MAX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from Symfony 5.x as it was deprecated in 6.x with no suitable replacement.
|
||||||
|
*
|
||||||
|
* RetryTillSaveStore is a PersistingStoreInterface implementation which decorate a non blocking
|
||||||
|
* PersistingStoreInterface to provide a blocking storage.
|
||||||
|
*
|
||||||
|
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||||
|
*/
|
||||||
|
final class RetryTillSaveStore implements BlockingStoreInterface, LoggerAwareInterface
|
||||||
|
{
|
||||||
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $retrySleep Duration in ms between 2 retry
|
||||||
|
* @param int $retryCount Maximum amount of retry
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private readonly PersistingStoreInterface $decorated,
|
||||||
|
private readonly int $retrySleep = 100,
|
||||||
|
private readonly int $retryCount = PHP_INT_MAX
|
||||||
|
) {
|
||||||
|
$this->logger = new NullLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save(Key $key): void
|
||||||
|
{
|
||||||
|
$this->decorated->save($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function waitAndSave(Key $key): void
|
||||||
|
{
|
||||||
|
$retry = 0;
|
||||||
|
$sleepRandomness = (int)($this->retrySleep / 10);
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
$this->decorated->save($key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (LockConflictedException) {
|
||||||
|
usleep(($this->retrySleep + random_int(-$sleepRandomness, $sleepRandomness)) * 1000);
|
||||||
|
}
|
||||||
|
} while (++$retry < $this->retryCount);
|
||||||
|
|
||||||
|
$this->logger?->warning(
|
||||||
|
'Failed to store the "{resource}" lock. Abort after {retry} retry.',
|
||||||
|
['resource' => $key, 'retry' => $retry]
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new LockConflictedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function putOffExpiration(Key $key, float $ttl): void
|
||||||
|
{
|
||||||
|
$this->decorated->putOffExpiration($key, $ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function delete(Key $key): void
|
||||||
|
{
|
||||||
|
$this->decorated->delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function exists(Key $key): bool
|
||||||
|
{
|
||||||
|
return $this->decorated->exists($key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\MessageQueue;
|
namespace App\MessageQueue;
|
||||||
|
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use Symfony\Component\Messenger\Envelope;
|
use Symfony\Component\Messenger\Envelope;
|
||||||
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
use Symfony\Component\Messenger\Exception\UnrecoverableMessageHandlingException;
|
||||||
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
||||||
|
@ -14,7 +14,7 @@ use Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp;
|
||||||
final class HandleUniqueMiddleware implements MiddlewareInterface
|
final class HandleUniqueMiddleware implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly LockFactory $lockFactory
|
protected LockFactory $lockFactory
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use App\Http\ServerRequest;
|
use App\Http\ServerRequest;
|
||||||
|
use App\Lock\LockFactory;
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
||||||
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace App\Service;
|
||||||
|
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Exception\RateLimitExceededException;
|
use App\Exception\RateLimitExceededException;
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use App\Version;
|
use App\Version;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\RequestOptions;
|
use GuzzleHttp\RequestOptions;
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace App\Service;
|
||||||
|
|
||||||
use App\Entity;
|
use App\Entity;
|
||||||
use App\Exception\RateLimitExceededException;
|
use App\Exception\RateLimitExceededException;
|
||||||
use App\LockFactory;
|
use App\Lock\LockFactory;
|
||||||
use App\Version;
|
use App\Version;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Psr7\UriResolver;
|
use GuzzleHttp\Psr7\UriResolver;
|
||||||
|
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
use App\Environment;
|
|
||||||
use App\Exception\SupervisorException;
|
use App\Exception\SupervisorException;
|
||||||
use App\Service\ServiceControl\ServiceData;
|
use App\Service\ServiceControl\ServiceData;
|
||||||
use Supervisor\Exception\Fault\BadNameException;
|
use Supervisor\Exception\Fault\BadNameException;
|
||||||
|
@ -27,7 +26,7 @@ final class ServiceControl
|
||||||
{
|
{
|
||||||
$services = [];
|
$services = [];
|
||||||
|
|
||||||
foreach ($this->getServiceNames() as $name => $description) {
|
foreach (self::getServiceNames() as $name => $description) {
|
||||||
try {
|
try {
|
||||||
$isRunning = in_array(
|
$isRunning = in_array(
|
||||||
$this->supervisor->getProcess($name)->getState(),
|
$this->supervisor->getProcess($name)->getState(),
|
||||||
|
@ -53,7 +52,7 @@ final class ServiceControl
|
||||||
|
|
||||||
public function restart(string $service): void
|
public function restart(string $service): void
|
||||||
{
|
{
|
||||||
$serviceNames = $this->getServiceNames();
|
$serviceNames = self::getServiceNames();
|
||||||
if (!isset($serviceNames[$service])) {
|
if (!isset($serviceNames[$service])) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
sprintf('Service "%s" is not managed by AzuraCast.', $service)
|
sprintf('Service "%s" is not managed by AzuraCast.', $service)
|
||||||
|
@ -72,9 +71,9 @@ final class ServiceControl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getServiceNames(): array
|
public static function getServiceNames(): array
|
||||||
{
|
{
|
||||||
$services = [
|
return [
|
||||||
'beanstalkd' => __('Message queue delivery service'),
|
'beanstalkd' => __('Message queue delivery service'),
|
||||||
'cron' => __('Runs routine synchronized tasks'),
|
'cron' => __('Runs routine synchronized tasks'),
|
||||||
'mariadb' => __('Database'),
|
'mariadb' => __('Database'),
|
||||||
|
@ -82,6 +81,7 @@ final class ServiceControl
|
||||||
'php-fpm' => __('PHP FastCGI Process Manager'),
|
'php-fpm' => __('PHP FastCGI Process Manager'),
|
||||||
'php-nowplaying' => __('Now Playing manager service'),
|
'php-nowplaying' => __('Now Playing manager service'),
|
||||||
'php-worker' => __('PHP queue processing worker'),
|
'php-worker' => __('PHP queue processing worker'),
|
||||||
|
'redis' => __('Cache'),
|
||||||
'sftpgo' => __('SFTP service'),
|
'sftpgo' => __('SFTP service'),
|
||||||
'centrifugo' => __('Live Now Playing updates'),
|
'centrifugo' => __('Live Now Playing updates'),
|
||||||
];
|
];
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
- supervisord
|
- supervisord
|
||||||
- nginx
|
- nginx
|
||||||
- php
|
- php
|
||||||
|
- redis
|
||||||
- beanstalkd
|
- beanstalkd
|
||||||
- sftpgo
|
- sftpgo
|
||||||
- mariadb
|
- mariadb
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
- name: Shut Down InfluxDB
|
||||||
|
service:
|
||||||
|
name: "influxdb"
|
||||||
|
state: stopped
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
|
- name: Remove InfluxDB if Present
|
||||||
|
apt:
|
||||||
|
name: "influxdb"
|
||||||
|
state: absent
|
||||||
|
force: true
|
||||||
|
purge: true
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
- name: Add Redis PPA repository (Focal)
|
||||||
|
apt_repository:
|
||||||
|
repo: "ppa:chris-lea/redis-server"
|
||||||
|
update_cache: true
|
||||||
|
when: ansible_distribution_release == 'focal'
|
||||||
|
|
||||||
|
- name: Install Redis
|
||||||
|
apt:
|
||||||
|
name: redis-server
|
||||||
|
|
||||||
|
- name: Install Redis Conf
|
||||||
|
template:
|
||||||
|
src: redis.conf.j2
|
||||||
|
dest: /etc/redis/redis.conf
|
||||||
|
force: true
|
||||||
|
owner: "redis"
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Install Redis Supervisord conf
|
||||||
|
template:
|
||||||
|
src: supervisor.conf.j2
|
||||||
|
dest: /etc/supervisor/conf.d/redis.conf
|
||||||
|
force: true
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Disable Redis services
|
||||||
|
service:
|
||||||
|
name: "{{ item }}"
|
||||||
|
enabled: false
|
||||||
|
state: stopped
|
||||||
|
ignore_errors: true
|
||||||
|
with_items:
|
||||||
|
- "redis-server"
|
||||||
|
- "redis"
|
|
@ -0,0 +1,14 @@
|
||||||
|
bind 127.0.0.1
|
||||||
|
protected-mode yes
|
||||||
|
port 6379
|
||||||
|
|
||||||
|
save ""
|
||||||
|
|
||||||
|
appendonly no
|
||||||
|
|
||||||
|
maxmemory 128mb
|
||||||
|
maxmemory-policy volatile-lfu
|
||||||
|
|
||||||
|
always-show-logo no
|
||||||
|
|
||||||
|
protected-mode no
|
|
@ -0,0 +1,6 @@
|
||||||
|
[program:redis]
|
||||||
|
command=/usr/bin/redis-server /etc/redis/redis.conf
|
||||||
|
user=redis
|
||||||
|
numprocs=1
|
||||||
|
autostart=true
|
||||||
|
autorestart=unexpected
|
|
@ -30,6 +30,9 @@
|
||||||
- role: "nginx"
|
- role: "nginx"
|
||||||
when: update_revision|int < 90
|
when: update_revision|int < 90
|
||||||
|
|
||||||
|
- role: "redis"
|
||||||
|
when: update_revision|int < 93
|
||||||
|
|
||||||
- role: "beanstalkd"
|
- role: "beanstalkd"
|
||||||
when: update_revision|int < 87
|
when: update_revision|int < 87
|
||||||
|
|
||||||
|
@ -41,6 +44,9 @@
|
||||||
|
|
||||||
- role: "composer"
|
- role: "composer"
|
||||||
|
|
||||||
|
- role: "influxdb"
|
||||||
|
when: update_revision|int < 58
|
||||||
|
|
||||||
- role: "ufw"
|
- role: "ufw"
|
||||||
when: update_revision|int < 86
|
when: update_revision|int < 86
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
unixsocket /run/redis/redis.sock
|
||||||
|
unixsocketperm 666
|
||||||
|
|
||||||
|
bind 127.0.0.1
|
||||||
|
port 6379
|
||||||
|
|
||||||
|
save ""
|
||||||
|
|
||||||
|
appendonly no
|
||||||
|
|
||||||
|
maxmemory 128mb
|
||||||
|
maxmemory-policy volatile-lfu
|
||||||
|
|
||||||
|
always-show-logo no
|
|
@ -0,0 +1,15 @@
|
||||||
|
[program:redis]
|
||||||
|
command=redis-server /etc/redis/redis.conf
|
||||||
|
user=redis
|
||||||
|
priority=100
|
||||||
|
numprocs=1
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
|
||||||
|
stdout_logfile=/var/azuracast/www_tmp/service_redis.log
|
||||||
|
stdout_logfile_maxbytes=5MB
|
||||||
|
stdout_logfile_backups=5
|
||||||
|
redirect_stderr=true
|
||||||
|
|
||||||
|
stdout_events_enabled = true
|
||||||
|
stderr_events_enabled = true
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
|
||||||
|
# Install common scripts
|
||||||
|
# cp -rT /bd_build/redis/scripts/ /usr/local/bin
|
||||||
|
|
||||||
|
cp -rT /bd_build/redis/startup_scripts/. /etc/my_init.d/
|
||||||
|
|
||||||
|
cp -rT /bd_build/redis/service.minimal/. /etc/supervisor/minimal.conf.d/
|
||||||
|
|
||||||
|
# cp -rT /bd_build/redis/service.full/. /etc/supervisor/full.conf.d/
|
||||||
|
|
||||||
|
# Run service setup for all setup scripts
|
||||||
|
for f in /bd_build/redis/setup/*.sh; do
|
||||||
|
bash "$f" -H
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
apt-get -y autoremove
|
||||||
|
apt-get clean
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
rm -rf /tmp/tmp*
|
||||||
|
|
||||||
|
chmod -R a+x /usr/local/bin
|
||||||
|
chmod -R +x /etc/my_init.d
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
apt-get install -y --no-install-recommends redis-server
|
||||||
|
|
||||||
|
cp /bd_build/redis/redis/redis.conf /etc/redis/redis.conf
|
||||||
|
chown redis:redis /etc/redis/redis.conf
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
bool() {
|
||||||
|
case "$1" in
|
||||||
|
Y* | y* | true | TRUE | 1) return 0 ;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# If Redis is expressly disabled or the host is anything but localhost, disable Redis on this container.
|
||||||
|
ENABLE_REDIS=${ENABLE_REDIS:-true}
|
||||||
|
|
||||||
|
if [ "$REDIS_HOST" != "localhost" ] || ! bool "$ENABLE_REDIS"; then
|
||||||
|
echo "Redis is disabled or host is not localhost; disabling Redis..."
|
||||||
|
rm -rf /etc/supervisor/minimal.conf.d/redis.conf
|
||||||
|
fi
|
Loading…
Reference in New Issue