PDO plugin: Reflections
* com.noccy.pdo: Implemented reflection for PDO databases, tables and columns. Reflectors for MySQL and Sqlite. * com.noccy.pdo: Added pdo:inspect command. * com.noccy.docker: Added basic stack management and commands. * com.noccy.docker: Moved commands to dedicated namespace. * Environment: readConfig and writeConfig helper added, with a flag to use the global config dir ~/.config/spark.
This commit is contained in:
24
plugins/com.noccy.docker/Commands/DockerBuildCommand.php
Normal file
24
plugins/com.noccy.docker/Commands/DockerBuildCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerBuildCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:build")
|
||||
->setDescription("Build an image");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
89
plugins/com.noccy.docker/Commands/DockerDbExportCommand.php
Normal file
89
plugins/com.noccy.docker/Commands/DockerDbExportCommand.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\Input;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerDbExportCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:db:export")
|
||||
->setDescription("Export a database")
|
||||
->addOption("mysql", null, InputOption::VALUE_NONE, "Export from a MySQL database")
|
||||
->addOption("dsn", null, InputOption::VALUE_REQUIRED, "Database DSN")
|
||||
->addOption("service", null, InputOption::VALUE_REQUIRED, "Service name in stack")
|
||||
->addOption("database", null, InputOption::VALUE_REQUIRED, "Database name")
|
||||
->addOption("output", "o", InputOption::VALUE_REQUIRED, "Output to file instead of stdout")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$docker = get_plugin("com.noccy.docker");
|
||||
|
||||
$service = $input->getOption("service");
|
||||
$dsn = $input->getOption("dsn");
|
||||
if ($service) {
|
||||
$stack = $docker->getComposeStack();
|
||||
if ($input->getOption("mysql")) {
|
||||
$dbtype = 'mysql';
|
||||
} else {
|
||||
$services = $stack->getServiceNames();
|
||||
if (!in_array($service, $services)) {
|
||||
$output->writeln("<error>Invalid service {$service}. Valid are ".join(", ", $services)."</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
$image = $stack->getService($service)->getImage();
|
||||
if (preg_match('/(mysql|mariadb)/i', $image)) {
|
||||
$dbtype = 'mysql';
|
||||
$env = $stack->getService($service)->getEnvironment();
|
||||
$database = $env['MYSQL_DATABASE']??null;
|
||||
if ($dbpass = $env['MYSQL_ROOT_PASSWORD']??null) {
|
||||
$dbuser = 'root';
|
||||
} else {
|
||||
$dbuser = $env['MYSQL_USER']??null;
|
||||
$dbpass = $env['MYSQL_PASSWORD']??null;
|
||||
}
|
||||
} else {
|
||||
$output->writeln("<error>Unable to determine database type from service</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
}
|
||||
$database = $database ?? $input->getOption("database");
|
||||
if (empty($database)) {
|
||||
$output->writeln("<error>No --database specified</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
switch ($dbtype) {
|
||||
case 'mysql':
|
||||
$cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database);
|
||||
break;
|
||||
}
|
||||
$this->exportFromService($service, $cmd, $output, $input->getOption("output"));
|
||||
} elseif ($dsn) {
|
||||
$url = parse_url($dsn);
|
||||
if (empty($url)) {
|
||||
$output->writeln("<error>Bad database DSN {$dsn}. Should look like mysql://user:pass@host:port/database</>");
|
||||
return Command::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function exportFromService(string $service, string $command, OutputInterface $output, ?string $outfile=null)
|
||||
{
|
||||
$cmd = sprintf("docker-compose exec -T %s %s", $service, $command);
|
||||
if ($outfile) {
|
||||
$cmd .= " > ".escapeshellarg($outfile);
|
||||
}
|
||||
$output->writeln(sprintf("→ <info>%s</>", $cmd));
|
||||
passthru($cmd);
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/Commands/DockerDownCommand.php
Normal file
24
plugins/com.noccy.docker/Commands/DockerDownCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerDownCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:down")
|
||||
->setDescription("Stop a stack or container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/Commands/DockerExecCommand.php
Normal file
24
plugins/com.noccy.docker/Commands/DockerExecCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerExecCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:exec")
|
||||
->setDescription("Execute scripts in a docker container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
70
plugins/com.noccy.docker/Commands/DockerStatusCommand.php
Normal file
70
plugins/com.noccy.docker/Commands/DockerStatusCommand.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerStatusCommand extends Command
|
||||
{
|
||||
const BULLET="\u{25cf}";
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:status")
|
||||
->setDescription("Show docker status");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
|
||||
exec("docker-compose ps -q", $ids, $ret);
|
||||
if (count($ids) === 0) return Command::SUCCESS;
|
||||
|
||||
exec("docker inspect ".join(" ",$ids), $out, $ret);
|
||||
$json = json_decode(join("", $out));
|
||||
|
||||
$stack = get_plugin('com.noccy.docker')->getComposeStack();
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setStyle("box");
|
||||
$table->setHeaders([ "Name", "Status", "Image", "Ports" ]);
|
||||
foreach ($json as $container) {
|
||||
$startedTs = preg_replace('/(\.([0-9]+)Z)$/', '+0100', $container->State->StartedAt);
|
||||
$s = date_parse($startedTs);
|
||||
$started = mktime($s['hour'], $s['minute'], $s['second'], $s['month'], $s['day'], $s['year']) + 3600;
|
||||
if ($container->State->Dead) {
|
||||
$status = "<fg=red>".self::BULLET."</> ".$container->State->Status;
|
||||
} elseif ($container->State->Restarting) {
|
||||
$status = "<fg=yellow>".self::BULLET."</> ".$container->State->Status;
|
||||
} elseif ($container->State->Running) {
|
||||
$elapsed = time() - $started;
|
||||
if ($elapsed > 60) {
|
||||
$em = floor($elapsed / 60);
|
||||
$es = $elapsed - ($em * 60);
|
||||
if ($em>60) {
|
||||
$eh = floor($em / 60);
|
||||
$em = $em - ($eh * 60);
|
||||
$elapsed = sprintf("%dh%dm%ds", $eh, $em, $es);
|
||||
} else {
|
||||
$elapsed = sprintf("%dm%ds", $em, $es);
|
||||
}
|
||||
} else {
|
||||
$elapsed = sprintf("%ds", $elapsed);
|
||||
}
|
||||
$status = "<fg=green>".self::BULLET."</> ".$container->State->Status." (<fg=green>{$elapsed}</>)";
|
||||
} else {
|
||||
$status = "<fg=red>".self::BULLET."</> ".$container->State->Status;
|
||||
}
|
||||
$ports = $container->Config->ExposedPorts??[];
|
||||
$ports = array_keys((array)$ports);
|
||||
$table->addRow([ $container->Name, $status, $container->Config->Image, join(", ", $ports) ]);
|
||||
}
|
||||
$table->render();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
24
plugins/com.noccy.docker/Commands/DockerUpCommand.php
Normal file
24
plugins/com.noccy.docker/Commands/DockerUpCommand.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class DockerUpCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:up")
|
||||
->setDescription("Start a stack or container");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$config = read_config("docker.json");
|
||||
print_r($config);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
44
plugins/com.noccy.docker/Commands/Stack/RegisterCommand.php
Normal file
44
plugins/com.noccy.docker/Commands/Stack/RegisterCommand.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands\Stack;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\Docker\Stack\StackManager;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class RegisterCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:stack:register")
|
||||
->setDescription("Register a stack");
|
||||
|
||||
$this->addArgument("options", InputArgument::IS_ARRAY, "key=value pairs of stack options");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$stacks = $this->getStackManager();
|
||||
|
||||
$opts = [];
|
||||
foreach ($input->getArgument("options") as $opt) {
|
||||
if (str_contains($opt, "=")) {
|
||||
[$k,$v] = explode("=", $opt, 2);
|
||||
$opts[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$root = $this->getEnvironment()->getProjectDirectory();
|
||||
$stacks->registerStack($root, $opts);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function getStackManager(): StackManager
|
||||
{
|
||||
return new StackManager($this->getEnvironment());
|
||||
}
|
||||
}
|
43
plugins/com.noccy.docker/Commands/Stack/StatusCommand.php
Normal file
43
plugins/com.noccy.docker/Commands/Stack/StatusCommand.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands\Stack;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\Docker\Stack\StackManager;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class StatusCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:stack:status")
|
||||
->setDescription("Show status on registered stacks");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$stackManager = $this->getStackManager();
|
||||
$stacks = $stackManager->getRegisteredStacks();
|
||||
|
||||
foreach ($stacks as $stack) {
|
||||
$output->writeln("<fg=yellow>\u{2bbb}</> <fg=white;options=bold>{$stack->getName()}</>");
|
||||
$table = $stack->getContainersTable($output);
|
||||
$table->setStyle('compact');
|
||||
$table->setColumnWidths([ 30, 20, 20, 0 ]);
|
||||
$table->render();
|
||||
$output->writeln("");
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function getStackManager(): StackManager
|
||||
{
|
||||
return new StackManager($this->getEnvironment());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\Docker\Commands\Stack;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\Docker\Stack\StackManager;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class UnregisterCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("docker:stack:unregister")
|
||||
->setDescription("Unregister a stack");
|
||||
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$stacks = $this->getStackManager();
|
||||
|
||||
$root = $this->getEnvironment()->getProjectDirectory();
|
||||
$stacks->removeStack($root);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function getStackManager(): StackManager
|
||||
{
|
||||
return new StackManager($this->getEnvironment());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user