Merge commit 'fa79a035eaa3510636839978c82ca56b5d4044fd'

This commit is contained in:
Buster "Silver Eagle" Neece 2022-05-25 20:49:09 -05:00
parent 693e4378f0
commit cbad2a0293
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
72 changed files with 476 additions and 877 deletions

View File

@ -41,33 +41,32 @@ COPY ./util/docker/common /bd_build/
RUN chmod a+x /bd_build/*.sh \
&& /bd_build/prepare.sh \
&& /bd_build/add_user.sh \
&& /bd_build/cleanup.sh
&& /bd_build/cleanup.sh \
&& rm -rf /bd_build
# Build each set of dependencies in their own step for cacheability.
ARG ARM_FULL_BUILD
COPY ./util/docker/supervisor /bd_build/supervisor/
RUN bash /bd_build/supervisor/setup.sh \
&& rm -rf /bd_build/supervisor
COPY ./util/docker/stations /bd_build/stations/
RUN bash /bd_build/stations/setup.sh \
&& bash /bd_build/cleanup.sh \
&& rm -rf /bd_build/stations
COPY ./util/docker/web /bd_build/web/
RUN bash /bd_build/web/setup.sh \
&& bash /bd_build/cleanup.sh \
&& rm -rf /bd_build/web
COPY ./util/docker/mariadb /bd_build/mariadb/
RUN bash /bd_build/mariadb/setup.sh \
&& bash /bd_build/cleanup.sh \
&& rm -rf /bd_build/mariadb
COPY ./util/docker/redis /bd_build/redis/
RUN bash /bd_build/redis/setup.sh \
&& bash /bd_build/cleanup.sh \
&& rm -rf /bd_build/redis
RUN bash /bd_build/post_setup.sh \
&& rm -rf /bd_build
#
# START Operations as `azuracast` user
#
@ -102,6 +101,7 @@ EXPOSE 8000-8999
ENV LANG="en_US.UTF-8" \
DOCKER_IS_STANDALONE="true" \
APPLICATION_ENV="production" \
MARIADB_AUTO_UPGRADE=1 \
MYSQL_HOST="localhost" \
MYSQL_PORT=3306 \
MYSQL_USER="azuracast" \
@ -122,5 +122,5 @@ ENV LANG="en_US.UTF-8" \
PROFILING_EXTENSION_HTTP_IP_WHITELIST=*
# Entrypoint and default command
ENTRYPOINT ["/usr/local/bin/my_init"]
ENTRYPOINT ["tini", "--", "/usr/local/bin/my_init"]
CMD ["--no-main-command"]

View File

@ -7,9 +7,6 @@ return function (App\Event\BuildConsoleCommands $event) {
'azuracast:backup' => Command\Backup\BackupCommand::class,
'azuracast:restore' => Command\Backup\RestoreCommand::class,
'azuracast:debug:optimize-tables' => Command\Debug\OptimizeTablesCommand::class,
'azuracast:internal:liquidsoap' => Command\Internal\LiquidsoapCommand::class,
'azuracast:internal:sftp-event' => Command\Internal\SftpEventCommand::class,
'azuracast:internal:sftp-auth' => Command\Internal\SftpAuthCommand::class,
'azuracast:internal:on-ssl-renewal' => Command\Internal\OnSslRenewal::class,
'azuracast:internal:ip' => Command\Internal\GetIpCommand::class,
'azuracast:locale:generate' => Command\Locale\GenerateCommand::class,

View File

@ -61,6 +61,12 @@ return static function (RouteCollectorProxy $app) {
}
)->add(Middleware\GetStation::class);
$group->post('/sftp-auth', Controller\Api\Internal\SftpAuthAction::class)
->setName('api:internal:sftp-auth');
$group->post('/sftp-event', Controller\Api\Internal\SftpEventAction::class)
->setName('api:internal:sftp-event');
$group->get('/relays', Controller\Api\Admin\RelaysController::class)
->setName('api:internal:relays')
->add(Middleware\RequireLogin::class);

View File

@ -1,100 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Console\Command\Internal;
use App\Console\Command\CommandAbstract;
use App\Entity;
use App\Entity\Repository\StationRepository;
use App\Radio\Backend\Liquidsoap\Command\AbstractCommand;
use App\Radio\Enums\LiquidsoapCommands;
use LogicException;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;
#[AsCommand(
name: 'azuracast:internal:liquidsoap',
description: 'Handle Liquidsoap API calls.',
)]
class LiquidsoapCommand extends CommandAbstract
{
public function __construct(
protected StationRepository $stationRepo,
protected ContainerInterface $di,
protected LoggerInterface $logger,
) {
parent::__construct();
}
protected function configure(): void
{
$this->addArgument('action', InputArgument::REQUIRED)
->addArgument('station-id', InputArgument::REQUIRED)
->addOption(
'as-autodj',
null,
InputOption::VALUE_NONE,
'Whether the task is executing as the actual AutoDJ or as a test.'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$stationId = $input->getArgument('station-id');
$action = $input->getArgument('action');
$asAutoDj = (bool)$input->getOption('as-autodj');
$payload = trim(getenv('PAYLOAD') ?: '');
if (!empty($payload)) {
$payload = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
} else {
$payload = [];
}
try {
$station = $this->stationRepo->findByIdentifier($stationId);
if (!($station instanceof Entity\Station)) {
throw new LogicException('Station not found.');
}
$command = LiquidsoapCommands::tryFrom($action);
if (null === $command || !$this->di->has($command->getClass())) {
throw new LogicException('Command not found.');
}
/** @var AbstractCommand $commandObj */
$commandObj = $this->di->get($command->getClass());
$result = $commandObj->run($station, $asAutoDj, $payload);
$io->writeln($result);
} catch (Throwable $e) {
$this->logger->error(
sprintf(
'Liquidsoap command "%s" error: %s',
$action,
$e->getMessage()
),
[
'station' => $stationId,
'payload' => $payload,
'as-autodj' => $asAutoDj,
]
);
$io->writeln('false');
return 1;
}
return 0;
}
}

View File

@ -2,45 +2,40 @@
declare(strict_types=1);
namespace App\Console\Command\Internal;
namespace App\Controller\Api\Internal;
use App\Console\Command\CommandAbstract;
use App\Entity\SftpUser;
use App\Http\Response;
use App\Http\ServerRequest;
use Brick\Math\BigInteger;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use const JSON_THROW_ON_ERROR;
#[AsCommand(
name: 'azuracast:internal:sftp-auth',
description: 'Attempt SFTP authentication.',
)]
class SftpAuthCommand extends CommandAbstract
final class SftpAuthAction
{
public function __construct(
protected EntityManagerInterface $em,
protected LoggerInterface $logger,
private readonly EntityManagerInterface $em,
private readonly LoggerInterface $logger,
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$errorResponse = json_encode(['username' => ''], JSON_THROW_ON_ERROR);
public function __invoke(
ServerRequest $request,
Response $response
): ResponseInterface {
$errorResponse = $response
->withStatus(500)
->withJson(['username' => '']);
$username = getenv('SFTPGO_AUTHD_USERNAME') ?: '';
$password = getenv('SFTPGO_AUTHD_PASSWORD') ?: '';
$pubKey = getenv('SFTPGO_AUTHD_PUBLIC_KEY') ?: '';
$parsedBody = $request->getParsedBody();
$username = $parsedBody['username'] ?? '';
$password = $parsedBody['password'] ?? '';
$pubKey = $parsedBody['public_key'] ?? '';
if (empty($username)) {
$io->write($errorResponse);
return 1;
return $errorResponse;
}
$sftpUser = $this->em->getRepository(SftpUser::class)->findOneBy(['username' => $username]);
@ -52,8 +47,7 @@ class SftpAuthCommand extends CommandAbstract
)
);
$io->write($errorResponse);
return 1;
return $errorResponse;
}
if (!$sftpUser->authenticate($password, $pubKey)) {
@ -68,8 +62,7 @@ class SftpAuthCommand extends CommandAbstract
]
);
$io->write($errorResponse);
return 1;
return $errorResponse;
}
$storageLocation = $sftpUser->getStation()->getMediaStorageLocation();
@ -83,8 +76,7 @@ class SftpAuthCommand extends CommandAbstract
)
);
$io->write($errorResponse);
return 1;
return $errorResponse;
}
$quotaRaw = $storageLocation->getStorageQuotaBytes();
@ -105,7 +97,6 @@ class SftpAuthCommand extends CommandAbstract
],
];
$io->write(json_encode($row, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK));
return 0;
return $response->withJson($row);
}
}

View File

@ -2,90 +2,104 @@
declare(strict_types=1);
namespace App\Console\Command\Internal;
namespace App\Controller\Api\Internal;
use App\Console\Command\CommandAbstract;
use App\Entity;
use App\Entity\SftpUser;
use App\Entity\StorageLocation;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Media\BatchUtilities;
use App\Message;
use App\Message\AddNewMediaMessage;
use Doctrine\ORM\EntityManagerInterface;
use League\Flysystem\PathPrefixer;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Messenger\MessageBus;
#[AsCommand(
name: 'azuracast:internal:sftp-event',
description: 'Send upcoming song feedback from the AutoDJ back to AzuraCast.',
)]
class SftpEventCommand extends CommandAbstract
final class SftpEventAction
{
public function __construct(
protected EntityManagerInterface $em,
protected MessageBus $messageBus,
protected LoggerInterface $logger,
protected BatchUtilities $batchUtilities,
private readonly EntityManagerInterface $em,
private readonly MessageBus $messageBus,
private readonly LoggerInterface $logger,
private readonly BatchUtilities $batchUtilities,
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$action = getenv('SFTPGO_ACTION') ?: null;
$username = getenv('SFTPGO_ACTION_USERNAME') ?: null;
$path = getenv('SFTPGO_ACTION_PATH') ?: null;
$targetPath = getenv('SFTPGO_ACTION_TARGET') ?: null;
$sshCmd = getenv('SFTPGO_ACTION_SSH_CMD') ?: null;
public function __invoke(
ServerRequest $request,
Response $response
): ResponseInterface {
$errorResponse = $response->withStatus(500)->withJson(['success' => false]);
$parsedBody = $request->getParsedBody();
$action = $parsedBody['action'] ?? null;
$username = $parsedBody['username'] ?? null;
$path = $parsedBody['path'] ?? null;
$targetPath = $parsedBody['target_path'] ?? null;
$sshCmd = $parsedBody['ssh_cmd'] ?? null;
$this->logger->notice(
'SFTP file event triggered',
[
'action' => $action,
'username' => $username,
'path' => $path,
'action' => $action,
'username' => $username,
'path' => $path,
'targetPath' => $targetPath,
'sshCmd' => $sshCmd,
'sshCmd' => $sshCmd,
]
);
// Determine which station the username belongs to.
$sftpUser = $this->em->getRepository(Entity\SftpUser::class)->findOneBy(
$sftpUser = $this->em->getRepository(SftpUser::class)->findOneBy(
[
'username' => $username,
]
);
if (!$sftpUser instanceof Entity\SftpUser) {
if (!$sftpUser instanceof SftpUser) {
$this->logger->error('SFTP Username not found.', ['username' => $username]);
return 1;
return $errorResponse;
}
$storageLocation = $sftpUser->getStation()->getMediaStorageLocation();
if (!$storageLocation->isLocal()) {
$this->logger->error(sprintf('Storage location "%s" is not local.', $storageLocation));
return 1;
return $errorResponse;
}
if (null === $path) {
$this->logger->error('No path specified for action.');
return 1;
return $errorResponse;
}
return match ($action) {
'upload' => $this->handleNewUpload($storageLocation, $path),
'pre-delete' => $this->handleDelete($storageLocation, $path),
'rename' => $this->handleRename($storageLocation, $path, $targetPath),
default => 1,
};
try {
match ($action) {
'upload' => $this->handleNewUpload($storageLocation, $path),
'pre-delete' => $this->handleDelete($storageLocation, $path),
'rename' => $this->handleRename($storageLocation, $path, $targetPath),
default => null,
};
return $response->withJson(['success' => true]);
} catch (\Throwable $e) {
$this->logger->error(
sprintf('SFTP Event: %s', $e->getMessage()),
[
'exception' => $e,
]
);
return $errorResponse;
}
}
protected function handleNewUpload(
Entity\StorageLocation $storageLocation,
private function handleNewUpload(
StorageLocation $storageLocation,
string $path
): int {
): void {
$pathPrefixer = new PathPrefixer($storageLocation->getPath(), DIRECTORY_SEPARATOR);
$relativePath = $pathPrefixer->stripPrefix($path);
@ -93,23 +107,21 @@ class SftpEventCommand extends CommandAbstract
'Processing new SFTP upload.',
[
'storageLocation' => (string)$storageLocation,
'path' => $relativePath,
'path' => $relativePath,
]
);
$message = new Message\AddNewMediaMessage();
$message = new AddNewMediaMessage();
$message->storage_location_id = $storageLocation->getIdRequired();
$message->path = $relativePath;
$this->messageBus->dispatch($message);
return 0;
}
protected function handleDelete(
Entity\StorageLocation $storageLocation,
private function handleDelete(
StorageLocation $storageLocation,
string $path
): int {
): void {
$pathPrefixer = new PathPrefixer($storageLocation->getPath(), DIRECTORY_SEPARATOR);
$relativePath = $pathPrefixer->stripPrefix($path);
@ -117,7 +129,7 @@ class SftpEventCommand extends CommandAbstract
'Processing SFTP file/folder deletion.',
[
'storageLocation' => (string)$storageLocation,
'path' => $relativePath,
'path' => $relativePath,
]
);
@ -140,18 +152,15 @@ class SftpEventCommand extends CommandAbstract
);
$fs->delete($relativePath);
return 0;
}
protected function handleRename(
Entity\StorageLocation $storageLocation,
private function handleRename(
StorageLocation $storageLocation,
string $path,
?string $newPath
): int {
): void {
if (null === $newPath) {
$this->logger->error('No new path specified for rename.');
return 1;
throw new \LogicException('No new path specified for rename.');
}
$pathPrefixer = new PathPrefixer($storageLocation->getPath(), DIRECTORY_SEPARATOR);
@ -163,8 +172,8 @@ class SftpEventCommand extends CommandAbstract
'Processing SFTP file/folder rename.',
[
'storageLocation' => (string)$storageLocation,
'from' => $from,
'to' => $to,
'from' => $from,
'to' => $to,
]
);
@ -173,7 +182,5 @@ class SftpEventCommand extends CommandAbstract
$to,
$storageLocation
);
return 0;
}
}

View File

@ -1,16 +1,14 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install sudo
apt-get install -y --no-install-recommends sudo
# Workaround for sudo errors in containers, see: https://github.com/sudo-project/sudo/issues/42
echo "Set disable_coredump false" >> /etc/sudo.conf
adduser --home /var/azuracast --disabled-password --gecos "" azuracast
usermod -aG docker_env azuracast
usermod -aG www-data azuracast
mkdir -p /var/azuracast/www /var/azuracast/stations /var/azuracast/servers/shoutcast2 \

View File

@ -1,22 +0,0 @@
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
minimal_apt_get_install='apt-get install -y --no-install-recommends'
install_without_postinst() {
local PACKAGE
PACKAGE=$1
mkdir -p /tmp/install_$PACKAGE
cd /tmp/install_$PACKAGE
apt-get download $PACKAGE
dpkg --unpack $PACKAGE*.deb
rm -f /var/lib/dpkg/info/$PACKAGE.postinst
dpkg --configure $PACKAGE
apt-get install -yf #To fix dependencies
cd /
rm -rf /tmp/install_$PACKAGE
}

View File

@ -1,11 +1,9 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
apt-get -y autoremove
apt-get clean
rm -rf /var/lib/apt/lists/*
rm -rf /tmp/tmp*

View File

@ -1,16 +0,0 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
chmod -R a+x /usr/local/bin
chmod -R +x /etc/my_init.d
chmod -R +x /etc/my_init.pre_shutdown.d
chmod -R +x /etc/my_init.post_shutdown.d
ln -s /etc/service.minimal/* /etc/service
ln -s /etc/service.full/* /etc/service
chmod -R +x /etc/service.minimal
chmod -R +x /etc/service.full
chmod -R +x /etc/service

View File

@ -1,14 +1,11 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
## Prevent initramfs updates from trying to run grub and lilo.
## https://journal.paul.querna.org/articles/2013/10/15/docker-ubuntu-on-rackspace/
## http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=594189
export INITRD=no
mkdir -p /etc/container_environment
echo -n no > /etc/container_environment/INITRD
# Add default timezone.
echo "UTC" > /etc/timezone
@ -35,46 +32,29 @@ dpkg-divert --local --rename --add /usr/bin/ischroot
ln -sf /bin/true /usr/bin/ischroot
# apt-utils fix for Ubuntu 16.04
$minimal_apt_get_install apt-utils
apt-get install -y --no-install-recommends apt-utils
## Install HTTPS support for APT.
$minimal_apt_get_install apt-transport-https ca-certificates
apt-get install -y --no-install-recommends apt-transport-https ca-certificates
## Install add-apt-repository
$minimal_apt_get_install software-properties-common
apt-get install -y --no-install-recommends software-properties-common
## Upgrade all packages.
apt-get dist-upgrade -y --no-install-recommends -o Dpkg::Options::="--force-confold"
## Fix locale.
$minimal_apt_get_install language-pack-en
apt-get install -y --no-install-recommends language-pack-en
locale-gen en_US
update-locale LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8
echo -n en_US.UTF-8 > /etc/container_environment/LANG
echo -n en_US.UTF-8 > /etc/container_environment/LC_CTYPE
# Make init folders
mkdir -p /etc/my_init.d
mkdir -p /etc/my_init.pre_shutdown.d
mkdir -p /etc/my_init.post_shutdown.d
mkdir -p /etc/container_environment
touch /etc/container_environment.sh
touch /etc/container_environment.json
chmod 700 /etc/container_environment
groupadd -g 8377 docker_env
chown :docker_env /etc/container_environment.sh /etc/container_environment.json
chmod 640 /etc/container_environment.sh /etc/container_environment.json
ln -s /etc/container_environment.sh /etc/profile.d/
# Install runit and other common scripts.
$minimal_apt_get_install runit gosu curl wget tar zip unzip git rsync tzdata gpg-agent openssh-client
# Install other common scripts.
apt-get install -y --no-install-recommends tini gosu curl wget tar zip unzip git rsync tzdata gpg-agent openssh-client
# Add scripts
cp -rT /bd_build/scripts/ /usr/local/bin
chmod -R a+x /usr/local/bin
mkdir -p /etc/service.full/
mkdir -p /etc/service.minimal/

View File

@ -1,402 +1,29 @@
#!/usr/bin/python3 -u
# -*- coding: utf-8 -*-
#!/bin/bash
import argparse
import errno
import json
import os
import os.path
import re
import signal
import stat
import sys
import time
# Write environment variables to script
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
chmod 744 /container.env
ENV_INIT_DIRECTORY = os.environ.get('ENV_INIT_DIRECTORY', '/etc/my_init.d')
# Run startup scripts
for f in /etc/my_init.d/*.sh; do
echo "** Running startup script '$f'..."
bash "$f" -H
echo "** Startup script complete."
done
KILL_PROCESS_TIMEOUT = int(os.environ.get('KILL_PROCESS_TIMEOUT', 30))
KILL_ALL_PROCESSES_TIMEOUT = int(os.environ.get('KILL_ALL_PROCESSES_TIMEOUT', 30))
echo "** Running Supervisord..."
LOG_LEVEL_ERROR = 1
LOG_LEVEL_WARN = 1
LOG_LEVEL_INFO = 2
LOG_LEVEL_DEBUG = 3
if [ "$1" = '--no-main-command' ]; then
exec /usr/bin/supervisord -c /etc/supervisor/supervisord-full.conf --nodaemon
fi
SHENV_NAME_WHITELIST_REGEX = re.compile('\W')
/usr/bin/supervisord -c /etc/supervisor/supervisord-minimal.conf
log_level = None
echo "** Supervisord started."
echo "** Running uptime_wait..."
terminated_child_processes = {}
uptime_wait
_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
echo "** Services up and ready!"
class AlarmException(Exception):
pass
def error(message):
if log_level >= LOG_LEVEL_ERROR:
sys.stderr.write("*** %s\n" % message)
def warn(message):
if log_level >= LOG_LEVEL_WARN:
sys.stderr.write("*** %s\n" % message)
def info(message):
if log_level >= LOG_LEVEL_INFO:
sys.stderr.write("*** %s\n" % message)
def debug(message):
if log_level >= LOG_LEVEL_DEBUG:
sys.stderr.write("*** %s\n" % message)
def ignore_signals_and_raise_keyboard_interrupt(signame):
signal.signal(signal.SIGTERM, signal.SIG_IGN)
signal.signal(signal.SIGINT, signal.SIG_IGN)
raise KeyboardInterrupt(signame)
def raise_alarm_exception():
raise AlarmException('Alarm')
def listdir(path):
try:
result = os.stat(path)
except OSError:
return []
if stat.S_ISDIR(result.st_mode):
return sorted(os.listdir(path))
else:
return []
def is_exe(path):
try:
return os.path.isfile(path) and os.access(path, os.X_OK)
except OSError:
return False
def import_envvars(clear_existing_environment=True, override_existing_environment=True):
if not os.path.exists("/etc/container_environment"):
return
new_env = {}
for envfile in listdir("/etc/container_environment"):
name = os.path.basename(envfile)
with open("/etc/container_environment/" + envfile, "r") as f:
# Text files often end with a trailing newline, which we
# don't want to include in the env variable value. See
# https://github.com/phusion/baseimage-docker/pull/49
value = re.sub('\n\Z', '', f.read())
new_env[name] = value
if clear_existing_environment:
os.environ.clear()
for name, value in new_env.items():
if override_existing_environment or name not in os.environ:
os.environ[name] = value
def export_envvars(to_dir=True):
if not os.path.exists("/etc/container_environment"):
return
shell_dump = ""
for name, value in os.environ.items():
if name in ['HOME', 'USER', 'GROUP', 'UID', 'GID', 'SHELL']:
continue
if to_dir:
with open("/etc/container_environment/" + name, "w") as f:
f.write(value)
shell_dump += "export " + sanitize_shenvname(name) + "=" + shquote(value) + "\n"
with open("/etc/container_environment.sh", "w") as f:
f.write(shell_dump)
with open("/etc/container_environment.json", "w") as f:
f.write(json.dumps(dict(os.environ)))
def shquote(s):
"""Return a shell-escaped version of the string *s*."""
if not s:
return "''"
if _find_unsafe(s) is None:
return s
# use single quotes, and put single quotes into double quotes
# the string $'b is then quoted as '$'"'"'b'
return "'" + s.replace("'", "'\"'\"'") + "'"
def sanitize_shenvname(s):
"""Return string with [0-9a-zA-Z_] characters"""
return re.sub(SHENV_NAME_WHITELIST_REGEX, "_", s)
# Waits for the child process with the given PID, while at the same time
# reaping any other child processes that have exited (e.g. adopted child
# processes that have terminated).
def waitpid_reap_other_children(pid):
global terminated_child_processes
status = terminated_child_processes.get(pid)
if status:
# A previous call to waitpid_reap_other_children(),
# with an argument not equal to the current argument,
# already waited for this process. Return the status
# that was obtained back then.
del terminated_child_processes[pid]
return status
done = False
status = None
while not done:
try:
# https://github.com/phusion/baseimage-docker/issues/151#issuecomment-92660569
this_pid, status = os.waitpid(pid, os.WNOHANG)
if this_pid == 0:
this_pid, status = os.waitpid(-1, 0)
if this_pid == pid:
done = True
else:
# Save status for later.
terminated_child_processes[this_pid] = status
except OSError as e:
if e.errno == errno.ECHILD or e.errno == errno.ESRCH:
return None
else:
raise
return status
def stop_child_process(name, pid, signo=signal.SIGTERM, time_limit=KILL_PROCESS_TIMEOUT):
info("Shutting down %s (PID %d)..." % (name, pid))
try:
os.kill(pid, signo)
except OSError:
pass
signal.alarm(time_limit)
try:
try:
waitpid_reap_other_children(pid)
except OSError:
pass
except AlarmException:
warn("%s (PID %d) did not shut down in time. Forcing it to exit." % (name, pid))
try:
os.kill(pid, signal.SIGKILL)
except OSError:
pass
try:
waitpid_reap_other_children(pid)
except OSError:
pass
finally:
signal.alarm(0)
def run_command_killable(*argv):
filename = argv[0]
status = None
pid = os.spawnvp(os.P_NOWAIT, filename, argv)
try:
status = waitpid_reap_other_children(pid)
except BaseException:
warn("An error occurred. Aborting.")
stop_child_process(filename, pid)
raise
if status != 0:
if status is None:
error("%s exited with unknown status\n" % filename)
else:
error("%s failed with status %d\n" % (filename, os.WEXITSTATUS(status)))
sys.exit(1)
def run_command_killable_and_import_envvars(*argv):
run_command_killable(*argv)
import_envvars()
export_envvars(False)
def kill_all_processes(time_limit):
info("Killing all processes...")
try:
os.kill(-1, signal.SIGTERM)
except OSError:
pass
signal.alarm(time_limit)
try:
# Wait until no more child processes exist.
done = False
while not done:
try:
os.waitpid(-1, 0)
except OSError as e:
if e.errno == errno.ECHILD:
done = True
else:
raise
except AlarmException:
warn("Not all processes have exited in time. Forcing them to exit.")
try:
os.kill(-1, signal.SIGKILL)
except OSError:
pass
finally:
signal.alarm(0)
def run_startup_files():
# Run ENV_INIT_DIRECTORY/*
for name in listdir(ENV_INIT_DIRECTORY):
filename = os.path.join(ENV_INIT_DIRECTORY, name)
if is_exe(filename):
info("Running %s..." % filename)
run_command_killable_and_import_envvars(filename)
# Run /etc/rc.local.
if is_exe("/etc/rc.local"):
info("Running /etc/rc.local...")
run_command_killable_and_import_envvars("/etc/rc.local")
def run_pre_shutdown_scripts():
debug("Running pre-shutdown scripts...")
# Run /etc/my_init.pre_shutdown.d/*
for name in listdir("/etc/my_init.pre_shutdown.d"):
filename = "/etc/my_init.pre_shutdown.d/" + name
if is_exe(filename):
info("Running %s..." % filename)
run_command_killable(filename)
def run_post_shutdown_scripts():
debug("Running post-shutdown scripts...")
# Run /etc/my_init.post_shutdown.d/*
for name in listdir("/etc/my_init.post_shutdown.d"):
filename = "/etc/my_init.post_shutdown.d/" + name
if is_exe(filename):
info("Running %s..." % filename)
run_command_killable(filename)
def start_runit(runit_services_dir):
info("Booting runit daemon...")
pid = os.spawnl(os.P_NOWAIT, "/usr/bin/runsvdir", "/usr/bin/runsvdir", "-P", runit_services_dir)
info("Runit started as PID %d" % pid)
return pid
def wait_for_runit_or_interrupt(pid):
status = waitpid_reap_other_children(pid)
return (True, status)
def shutdown_runit_services(runit_services_dir, quiet=False):
if not quiet:
debug("Begin shutting down runit services...")
os.system("/usr/bin/sv -w %d force-stop %s/* > /dev/null" % (KILL_PROCESS_TIMEOUT, runit_services_dir))
def wait_for_runit_services(runit_services_dir):
debug("Waiting for runit services to exit...")
done = False
while not done:
done = os.system("/usr/bin/sv status %s/* | grep -q '^run:'" % runit_services_dir) != 0
if not done:
time.sleep(0.1)
# According to https://github.com/phusion/baseimage-docker/issues/315
# there is a bug or race condition in Runit, causing it
# not to shutdown services that are already being started.
# So during shutdown we repeatedly instruct Runit to shutdown
# services.
shutdown_runit_services(runit_services_dir, True)
def main(args):
import_envvars(False, False)
export_envvars()
if not args.skip_startup_files:
run_startup_files()
runit_exited = False
exit_code = None
if not args.skip_runit:
runit_services_dir = '/etc/service.minimal'
if len(args.main_command) == 0 or args.no_main_command:
runit_services_dir = '/etc/service'
runit_pid = start_runit(runit_services_dir)
try:
exit_status = None
if len(args.main_command) == 0 or args.no_main_command:
runit_exited, exit_code = wait_for_runit_or_interrupt(runit_pid)
if runit_exited:
if exit_code is None:
info("Runit exited with unknown status")
exit_status = 1
else:
exit_status = os.WEXITSTATUS(exit_code)
info("Runit exited with status %d" % exit_status)
else:
main_command = ["uptime_wait"] + args.main_command
info("Running %s..." % " ".join(main_command))
pid = os.spawnvp(os.P_NOWAIT, main_command[0], main_command)
try:
exit_code = waitpid_reap_other_children(pid)
if exit_code is None:
info("%s exited with unknown status." % main_command[0])
exit_status = 1
else:
exit_status = os.WEXITSTATUS(exit_code)
info("%s exited with status %d." % (main_command[0], exit_status))
except KeyboardInterrupt:
stop_child_process(main_command[0], pid)
raise
except BaseException:
warn("An error occurred. Aborting.")
stop_child_process(main_command[0], pid)
raise
sys.exit(exit_status)
finally:
if not args.skip_runit:
run_pre_shutdown_scripts()
shutdown_runit_services(runit_services_dir)
if not runit_exited:
stop_child_process("runit daemon", runit_pid)
wait_for_runit_services(runit_services_dir)
run_post_shutdown_scripts()
# Parse options.
parser = argparse.ArgumentParser(description='Initialize the system.')
parser.add_argument('main_command', metavar='MAIN_COMMAND', type=str, nargs='*',
help='The main command to run. (default: runit)')
parser.add_argument('--no-main-command', dest='no_main_command',
action='store_const', const=True, default=False,
help='Flag to provide as main command in the absence of one.')
parser.add_argument('--skip-startup-files', dest='skip_startup_files',
action='store_const', const=True, default=False,
help='Skip running /etc/my_init.d/* and /etc/rc.local')
parser.add_argument('--skip-runit', dest='skip_runit',
action='store_const', const=True, default=False,
help='Do not run runit services')
parser.add_argument('--no-kill-all-on-exit', dest='kill_all_on_exit',
action='store_const', const=False, default=True,
help='Don\'t kill all processes on the system upon exiting')
parser.add_argument('--quiet', dest='log_level',
action='store_const', const=LOG_LEVEL_WARN, default=LOG_LEVEL_INFO,
help='Only print warnings and errors')
args = parser.parse_args()
log_level = args.log_level
if args.skip_runit and len(args.main_command) == 0:
error("When --skip-runit is given, you must also pass a main command.")
sys.exit(1)
# Run main function.
signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGTERM'))
signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGINT'))
signal.signal(signal.SIGALRM, lambda signum, frame: raise_alarm_exception())
try:
main(args)
except KeyboardInterrupt:
warn("Init system aborted.")
exit(2)
finally:
if args.kill_all_on_exit:
kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT)
exec "$@"

View File

@ -0,0 +1,12 @@
[program:mariadb]
command=mysqld
user=mysql
priority=100
numprocs=1
autostart=true
autorestart=unexpected
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,3 +0,0 @@
#!/bin/bash
exec mysqladmin ping -h localhost

View File

@ -1,5 +0,0 @@
#!/bin/bash
dockerize -template /etc/mysql/db.cnf.tmpl:/etc/mysql/conf.d/db.cnf
exec gosu mysql mysqld

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
apt-get update
@ -10,11 +9,20 @@ cp -rT /bd_build/mariadb/scripts/ /usr/local/bin
cp -rT /bd_build/mariadb/startup_scripts/. /etc/my_init.d/
cp -rT /bd_build/mariadb/service.minimal/. /etc/service.minimal/
cp -rT /bd_build/mariadb/service.minimal/. /etc/supervisor/minimal.conf.d/
# cp -rT /bd_build/mariadb/service.full/. /etc/service.full/
# cp -rT /bd_build/mariadb/service.full/. /etc/supervisor/full.conf.d/
# Run service setup for all setup scripts
for f in /bd_build/mariadb/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

View File

@ -1,9 +1,8 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install tzdata libjemalloc2 pwgen xz-utils zstd dirmngr apt-transport-https
apt-get install -y --no-install-recommends tzdata libjemalloc2 pwgen xz-utils zstd dirmngr apt-transport-https
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el,s390x] https://atl.mirrors.knownhost.com/mariadb/repo/10.7/ubuntu focal main'
@ -17,7 +16,7 @@ export MARIADB_MAJOR=10.7
} | debconf-set-selections;
apt update
$minimal_apt_get_install mariadb-server mariadb-backup socat
apt-get install -y --no-install-recommends mariadb-server mariadb-backup socat
# Temporary work around for MDEV-27980, closes #417
sed --follow-symlinks -i -e 's/--loose-disable-plugin-file-key-management//' /usr/bin/mysql_install_db
@ -43,9 +42,6 @@ else
sed -i -e '/includedir/ {N;s/\(.*\)\n\(.*\)/[mariadbd]\nskip-host-cache\nskip-name-resolve\n\n\2\n\1/}' /etc/mysql/mariadb.cnf;
fi
# Customizations to MariaDB
echo "1" >> /etc/container_environment/MARIADB_AUTO_UPGRADE
mkdir /docker-entrypoint-initdb.d
cp /bd_build/mariadb/mariadb/db.sql /docker-entrypoint-initdb.d/00-azuracast.sql

View File

@ -4,6 +4,5 @@
if [ "$MYSQL_HOST" != "localhost" ]; then
echo "MariaDB host is not localhost; disabling MariaDB..."
rm -rf /etc/service/mariadb
rm -rf /etc/service.minimal/mariadb
rm -rf /etc/supervisor/minimal.conf.d/mariadb.conf
fi

View File

@ -0,0 +1,8 @@
#!/bin/bash
if [ ! -f /etc/supervisor/minimal.conf.d/mariadb.conf ]; then
echo "MariaDB disabled. Skipping DB initialization..."
exit 0
fi
dockerize -template /etc/mysql/db.cnf.tmpl:/etc/mysql/conf.d/db.cnf

View File

@ -1,6 +1,6 @@
#!/bin/bash
if [ ! -f /etc/service/mariadb/run ]; then
if [ ! -f /etc/supervisor/minimal.conf.d/mariadb.conf ]; then
echo "MariaDB disabled. Skipping DB initialization..."
exit 0
fi

View File

@ -0,0 +1,11 @@
[program:redis]
command=redis-server /etc/redis/redis.conf
user=redis
numprocs=1
autostart=true
autorestart=unexpected
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,3 +0,0 @@
#!/bin/bash
exec gosu redis redis-server /etc/redis/redis.conf

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
apt-get update
@ -10,11 +9,20 @@ apt-get update
cp -rT /bd_build/redis/startup_scripts/. /etc/my_init.d/
cp -rT /bd_build/redis/service.minimal/. /etc/service.minimal/
cp -rT /bd_build/redis/service.minimal/. /etc/supervisor/minimal.conf.d/
# cp -rT /bd_build/redis/service.full/. /etc/service.full/
# 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

View File

@ -1,12 +1,11 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
add-apt-repository -y ppa:chris-lea/redis-server
apt-get update
$minimal_apt_get_install redis-server
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

View File

@ -12,6 +12,5 @@ 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/service/redis
rm -rf /etc/service.minimal/redis
rm -rf /etc/supervisor/minimal.conf.d/redis.conf
fi

View File

@ -1,5 +0,0 @@
#!/bin/sh
sv -w 45 check php-fpm || exit 1
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
apt-get update
@ -10,11 +9,20 @@ apt-get update
# cp -rT /bd_build/stations/startup_scripts/. /etc/my_init.d/
# cp -rT /bd_build/stations/service.minimal/. /etc/service.minimal/
# cp -rT /bd_build/stations/service.minimal/. /etc/supervisor/minimal.conf.d/
cp -rT /bd_build/stations/service.full/. /etc/service.full/
# cp -rT /bd_build/stations/service.full/. /etc/supervisor/full.conf.d/
# Run service setup for all setup scripts
for f in /bd_build/stations/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

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install flac
apt-get install -y --no-install-recommends flac

View File

@ -1,10 +1,9 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
# Only install Icecast deps (Icecast is handled by another container).
$minimal_apt_get_install libxml2 libxslt1-dev libvorbis-dev
apt-get install -y --no-install-recommends libxml2 libxslt1-dev libvorbis-dev
# SSL self-signed cert generation
$minimal_apt_get_install openssl
apt-get install -y --no-install-recommends openssl

View File

@ -1,10 +1,9 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
# Packages required by Liquidsoap
$minimal_apt_get_install libao-dev libasound2-dev libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev \
apt-get install -y --no-install-recommends libao-dev libasound2-dev libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev \
libavutil-dev libfaad-dev libfdk-aac-dev libflac-dev libfreetype-dev libgd-dev libjack-dev \
libjpeg-dev liblo-dev libmad0-dev libmagic-dev libmp3lame-dev libopus-dev libpng-dev libportaudio2 \
libpulse-dev libsamplerate0-dev libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev libshine-dev libsoundtouch-dev libspeex-dev \
@ -12,7 +11,7 @@ $minimal_apt_get_install libao-dev libasound2-dev libavcodec-dev libavdevice-dev
bubblewrap ffmpeg
# Optional audio plugins
$minimal_apt_get_install frei0r-plugins-dev ladspa-sdk multimedia-audio-plugins swh-plugins tap-plugins lsp-plugins-ladspa
apt-get install -y --no-install-recommends frei0r-plugins-dev ladspa-sdk multimedia-audio-plugins swh-plugins tap-plugins lsp-plugins-ladspa
# Per-architecture LS installs
ARCHITECTURE=amd64
@ -30,7 +29,7 @@ rm -f /tmp/liquidsoap.deb
ln -s /usr/bin/liquidsoap /usr/local/bin/liquidsoap
# else
# $minimal_apt_get_install build-essential libssl-dev libcurl4-openssl-dev m4 ocaml opam autoconf automake
# apt-get install -y --no-install-recommends build-essential libssl-dev libcurl4-openssl-dev m4 ocaml opam autoconf automake
# sudo -u azuracast bash /bd_build/stations/liquidsoap/build_as_azuracast.sh
# ln -s /var/azuracast/.opam/4.13.1/bin/liquidsoap /usr/local/bin/liquidsoap
# chmod a+x /usr/local/bin/liquidsoap

View File

@ -1,12 +0,0 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
# $minimal_apt_get_install python3-minimal python3-pip
# pip3 install setuptools supervisor
$minimal_apt_get_install supervisor
# mkdir -p /etc/supervisor
cp /bd_build/stations/supervisor/supervisord.conf /etc/supervisor/supervisord.conf

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install vorbis-tools
apt-get install -y --no-install-recommends vorbis-tools

View File

@ -0,0 +1,28 @@
#!/bin/bash
set -e
set -x
apt-get update
# Install common scripts
# cp -rT /bd_build/supervisor/scripts/ /usr/local/bin
# cp -rT /bd_build/supervisor/startup_scripts/. /etc/my_init.d/
# cp -rT /bd_build/supervisor/service.minimal/. /etc/supervisor/minimal.conf.d/
# cp -rT /bd_build/supervisor/service.full/. /etc/supervisor/full.conf.d/
# Run service setup for all setup scripts
for f in /bd_build/supervisor/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

View File

@ -0,0 +1,14 @@
#!/bin/bash
set -e
set -x
# apt-get install -y --no-install-recommends python3-minimal python3-pip
# pip3 install setuptools supervisor
apt-get install -y --no-install-recommends supervisor
mkdir -p /etc/supervisor
mkdir -p /etc/supervisor/full.conf.d/
mkdir -p /etc/supervisor/minimal.conf.d/
cp /bd_build/supervisor/supervisor/supervisord*.conf /etc/supervisor/

View File

@ -0,0 +1,2 @@
[include]
files = supervisord.conf minimal.conf.d/* full.conf.d/* /var/azuracast/stations/*/config/supervisord.conf

View File

@ -0,0 +1,2 @@
[include]
files = supervisord.conf minimal.conf.d/*

View File

@ -10,7 +10,6 @@ logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=info ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=true ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
@ -18,7 +17,4 @@ minprocs=200 ; (min. avail process descriptors;default 200)
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
[include]
files = /var/azuracast/stations/*/config/supervisord.conf conf.d/*
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket

View File

@ -1,2 +1,4 @@
SHELL=/bin/bash
BASH_ENV=/container.env
* * * * * root /usr/local/bin/cron_task azuracast_cli azuracast:sync:run
30 */6 * * * root /usr/local/bin/temp_cleanup

View File

@ -1,5 +0,0 @@
#!/usr/bin/env bash
source /etc/container_environment.sh
exec azuracast_cli azuracast:internal:sftp-auth "$@"

View File

@ -1,5 +0,0 @@
#!/usr/bin/env bash
source /etc/container_environment.sh
exec azuracast_cli azuracast:internal:sftp-event "$@"

View File

@ -1,5 +1,3 @@
#!/usr/bin/env bash
source /etc/container_environment.sh
exec gosu azuracast "$@" >/proc/1/fd/1 2>/proc/1/fd/2

View File

@ -1,9 +0,0 @@
#!/usr/bin/env bash
if [ `whoami` != 'azuracast' ]; then
exec gosu azuracast liquidsoap_cli "$@"
fi
source /etc/container_environment.sh
exec azuracast_cli azuracast:internal:liquidsoap "$@"

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
sv restart nginx
supervisorctl restart nginx
azuracast_cli azuracast:internal:on-ssl-renewal

View File

@ -1,7 +1,5 @@
#!/bin/bash
sv -w 45 check nginx || exit 1
# Acme loading script
# Uses code from:
# https://github.com/nginx-proxy/acme-companion/blob/main/app/letsencrypt_service
@ -204,7 +202,7 @@ function update_cert {
else
# If we did not get any email at all, use the default (empty mail) config
config_home="/etc/acme.sh/default"
fi
fi
local -n acme_ca_uri="ACME_CA_URI"
if [[ -z "$acme_ca_uri" ]]; then
@ -253,7 +251,7 @@ function update_cert {
# We're not using Zero SSL, register the ACME account using the provided email.
params_register_arr+=(--accountemail "$accountemail")
fi
# Account registration and update if required
if [[ ! -f "$account_file" ]]; then
params_register_arr=("${params_base_arr[@]}" "${params_register_arr[@]}")
@ -279,7 +277,7 @@ function update_cert {
params_issue_arr+=(--preferred-chain "$acme_preferred_chain")
fi
if [[ "$RENEW_PRIVATE_KEYS" != 'false' && "$REUSE_PRIVATE_KEYS" != 'true' ]]; then
params_issue_arr+=(--always-force-new-domain-key)
params_issue_arr+=(--always-force-new-domain-key)
fi
[[ "${2:-}" == "--force-renew" ]] && params_issue_arr+=(--force)

View File

@ -1,14 +1,6 @@
#!/bin/bash
source /etc/container_environment.sh
if [ -f /etc/service/mariadb/run ]; then
sv -w 30 check mariadb || exit 1
fi
if [ -f /etc/service/redis/run ]; then
sv -w 30 check redis || exit 1
fi
source /etc/php/.version
# Set up PHP config
dockerize -template "/etc/php/${PHP_VERSION}/fpm/05-azuracast.ini.tmpl:/etc/php/${PHP_VERSION}/fpm/conf.d/05-azuracast.ini" \

View File

@ -0,0 +1,11 @@
[program:acme]
command=run_acme_sh
priority=200
numprocs=1
autostart=true
autorestart=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -0,0 +1,11 @@
[program:beanstalkd]
command=beanstalkd -p 11300 -z 262140
user=azuracast
numprocs=1
autostart=true
autorestart=unexpected
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,5 +0,0 @@
#!/bin/bash
echo 'Spinning up Beanstalkd process...'
exec gosu azuracast beanstalkd -p 11300 -z 262140

View File

@ -0,0 +1,11 @@
[program:cron]
command=/usr/sbin/cron -f
priority=600
numprocs=1
autostart=true
autorestart=unexpected
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -0,0 +1,13 @@
[program:nginx]
command=nginx -g "daemon off;"
priority=100
numprocs=1
autostart=true
autorestart=unexpected
stopasgroup=true
killasgroup=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -0,0 +1,13 @@
[program:php-fpm]
command=run_php_fpm
priority=500
numprocs=1
autostart=true
autorestart=unexpected
stopasgroup=true
killasgroup=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,3 +0,0 @@
#!/bin/bash
FCGI_CONNECT=/var/run/php-fpm-www.sock exec php-fpm-healthcheck

View File

@ -0,0 +1,14 @@
[program:php-nowplaying]
command=php /var/azuracast/www/bin/console azuracast:sync:nowplaying
user=azuracast
priority=600
numprocs=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,5 +0,0 @@
#!/bin/bash
sv -w 30 check php-fpm || exit 1
exec gosu azuracast php /var/azuracast/www/bin/console azuracast:sync:nowplaying

View File

@ -0,0 +1,14 @@
[program:php-worker]
command=php /var/azuracast/www/bin/console queue:process --worker-name=app_worker_0
user=azuracast
priority=600
numprocs=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,5 +0,0 @@
#!/bin/bash
sv -w 30 check php-fpm || exit 1
exec gosu azuracast php /var/azuracast/www/bin/console queue:process --worker-name=app_worker_0

View File

@ -0,0 +1,15 @@
[program:sftpgo]
command=sftpgo --config-dir=/var/azuracast/sftpgo serve -l ""
dir=/var/azuracast/sftpgo
user=azuracast
priority=700
numprocs=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stdout_logfile=/proc/1/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/proc/1/fd/2
stderr_logfile_maxbytes=0

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
apt-get update
@ -10,11 +9,20 @@ cp -rT /bd_build/web/scripts/ /usr/local/bin
cp -rT /bd_build/web/startup_scripts/. /etc/my_init.d/
# cp -rT /bd_build/web/service.minimal/. /etc/service.minimal/
# cp -rT /bd_build/web/service.minimal/. /etc/supervisor/minimal.conf.d/
cp -rT /bd_build/web/service.full/. /etc/service.full/
cp -rT /bd_build/web/service.full/. /etc/supervisor/full.conf.d/
# Run service setup for all setup scripts
for f in /bd_build/web/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

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
# Get acme.sh ACME client source

View File

@ -1,9 +1,8 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
add-apt-repository -y ppa:chris-needham/ppa
apt-get update
$minimal_apt_get_install audiowaveform
apt-get install -y --no-install-recommends audiowaveform

View File

@ -1,8 +1,25 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install netbase
install_without_postinst() {
local PACKAGE
PACKAGE=$1
mkdir -p /tmp/install_$PACKAGE
cd /tmp/install_$PACKAGE
apt-get download $PACKAGE
dpkg --unpack $PACKAGE*.deb
rm -f /var/lib/dpkg/info/$PACKAGE.postinst
dpkg --configure $PACKAGE
apt-get install -yf #To fix dependencies
cd /
rm -rf /tmp/install_$PACKAGE
}
apt-get install -y --no-install-recommends netbase
install_without_postinst beanstalkd

View File

@ -1,8 +1,25 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
install_without_postinst() {
local PACKAGE
PACKAGE=$1
mkdir -p /tmp/install_$PACKAGE
cd /tmp/install_$PACKAGE
apt-get download $PACKAGE
dpkg --unpack $PACKAGE*.deb
rm -f /var/lib/dpkg/info/$PACKAGE.postinst
dpkg --configure $PACKAGE
apt-get install -yf #To fix dependencies
cd /
rm -rf /tmp/install_$PACKAGE
}
install_without_postinst cron
chmod 600 /etc/crontab

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
cd /tmp

View File

@ -1,9 +1,8 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install nginx nginx-common nginx-extras openssl
apt-get install -y --no-install-recommends nginx nginx-common nginx-extras openssl
# Install nginx and configuration
cp /bd_build/web/nginx/proxy_params.conf /etc/nginx/proxy_params

View File

@ -1,22 +1,21 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
PHP_VERSION=8.1
echo "${PHP_VERSION}" >> /etc/container_environment/PHP_VERSION
add-apt-repository -y ppa:ondrej/php
apt-get update
$minimal_apt_get_install php${PHP_VERSION}-fpm php${PHP_VERSION}-cli php${PHP_VERSION}-gd \
apt-get install -y --no-install-recommends php${PHP_VERSION}-fpm php${PHP_VERSION}-cli php${PHP_VERSION}-gd \
php${PHP_VERSION}-curl php${PHP_VERSION}-xml php${PHP_VERSION}-zip php${PHP_VERSION}-bcmath \
php${PHP_VERSION}-gmp php${PHP_VERSION}-mysqlnd php${PHP_VERSION}-mbstring php${PHP_VERSION}-intl \
php${PHP_VERSION}-redis php${PHP_VERSION}-maxminddb php${PHP_VERSION}-xdebug \
mariadb-client
# Copy PHP configuration
echo "PHP_VERSION=$PHP_VERSION" >> /etc/php/.version
mkdir -p /run/php
touch /run/php/php${PHP_VERSION}-fpm.pid
@ -27,14 +26,14 @@ cp /bd_build/web/php/www.conf.tmpl /etc/php/${PHP_VERSION}/fpm/www.conf.tmpl
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
# Download PHP-FPM healthcheck script
$minimal_apt_get_install libfcgi-bin
apt-get install -y --no-install-recommends libfcgi-bin
wget -O /usr/local/bin/php-fpm-healthcheck \
https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \
&& chmod +x /usr/local/bin/php-fpm-healthcheck
# Install PHP SPX profiler
$minimal_apt_get_install php${PHP_VERSION}-dev zlib1g-dev build-essential
apt-get install -y --no-install-recommends php${PHP_VERSION}-dev zlib1g-dev build-essential
cd /bd_build
git clone https://github.com/NoiseByNorthwest/php-spx.git

View File

@ -1,12 +1,11 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
add-apt-repository -y ppa:sftpgo/sftpgo
apt-get update
$minimal_apt_get_install sftpgo
apt-get install -y --no-install-recommends sftpgo
mkdir -p /var/azuracast/sftpgo/persist /var/azuracast/sftpgo/backups

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install tmpreaper
apt-get install -y --no-install-recommends tmpreaper

View File

@ -1,6 +1,5 @@
#!/bin/bash
set -e
source /bd_build/buildconfig
set -x
$minimal_apt_get_install zstd
apt-get install -y --no-install-recommends zstd

View File

@ -1,53 +1,53 @@
{
"common": {
"idle_timeout": 15,
"upload_mode": 2,
"setstat_mode": 1,
"actions": {
"execute_on": [
"upload",
"pre-delete",
"rename"
],
"hook": "/usr/local/bin/azuracast_sftp_event"
},
"defender": {
"enabled": true,
"driver": "memory"
}
"common": {
"idle_timeout": 15,
"upload_mode": 2,
"setstat_mode": 1,
"actions": {
"execute_on": [
"upload",
"pre-delete",
"rename"
],
"hook": "http://127.0.0.1:6010/api/internal/sftp-event"
},
"sftpd": {
"bindings": [
{
"port": 2022,
"address": "",
"apply_proxy_config": true
}
],
"host_keys": [
"persist/id_rsa",
"persist/id_ecdsa",
"persist/id_ed25519"
],
"enable_scp": true
},
"httpd": {
"bindings": [
{
"port": 0
}
],
"templates_path": "/usr/share/sftpgo/templates",
"static_files_path": "/usr/share/sftpgo/static"
},
"telemetry": {
"bind_port": 0
},
"data_provider": {
"driver": "bolt",
"name": "sftpgo.db",
"users_base_dir": "/var/azuracast/stations",
"external_auth_hook": "/usr/local/bin/azuracast_sftp_auth",
"external_auth_scope": 0
"defender": {
"enabled": true,
"driver": "memory"
}
},
"sftpd": {
"bindings": [
{
"port": 2022,
"address": "",
"apply_proxy_config": true
}
],
"host_keys": [
"persist/id_rsa",
"persist/id_ecdsa",
"persist/id_ed25519"
],
"enable_scp": true
},
"httpd": {
"bindings": [
{
"port": 0
}
],
"templates_path": "/usr/share/sftpgo/templates",
"static_files_path": "/usr/share/sftpgo/static"
},
"telemetry": {
"bind_port": 0
},
"data_provider": {
"driver": "bolt",
"name": "sftpgo.db",
"users_base_dir": "/var/azuracast/stations",
"external_auth_hook": "http://127.0.0.1:6010/api/internal/sftp-auth",
"external_auth_scope": 0
}
}

View File

@ -1,10 +1,6 @@
#!/bin/sh
sv -w 30 check php-fpm || exit 1
#!/bin/bash
# Touch cron files to fix 'NUMBER OF HARD LINKS > 1' issue. See https://github.com/phusion/baseimage-docker/issues/198
touch -c /var/spool/cron/crontabs/*
touch -c /etc/crontab
touch -c /etc/cron.*/*
exec /usr/sbin/cron -f

View File

@ -1,9 +1,7 @@
#!/bin/bash
sv -w 30 check php-fpm || exit 1
REDIS_LOCAL=false
if [ -f /etc/service/redis/run ]; then
if [ -f /etc/supervisor/minimal.conf.d/redis.conf ]; then
REDIS_LOCAL=true
fi
export REDIS_LOCAL
@ -11,5 +9,3 @@ export REDIS_LOCAL
# Copy the nginx template to its destination.
dockerize -template "/etc/nginx/nginx.conf.tmpl:/etc/nginx/nginx.conf" \
-template "/etc/nginx/azuracast.conf.tmpl:/etc/nginx/sites-available/default"
exec nginx -g "daemon off;"

View File

@ -1,7 +1,5 @@
#!/bin/bash
echo 'Spinning up SFTP process...'
if [[ ! -f /var/azuracast/sftpgo/persist/id_rsa ]]; then
ssh-keygen -t rsa -b 4096 -f /var/azuracast/sftpgo/persist/id_rsa -q -N ""
fi
@ -15,7 +13,3 @@ if [[ ! -f /var/azuracast/sftpgo/persist/id_ed25519 ]]; then
fi
chown -R azuracast:azuracast /var/azuracast/sftpgo/persist
cd /var/azuracast/sftpgo
exec sudo -E -u azuracast sftpgo --config-dir=/var/azuracast/sftpgo serve -l "" > /proc/1/fd/1 2> /proc/1/fd/2