diff options
author | Daniel Weipert <code@drogueronin.de> | 2021-04-20 12:45:44 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2021-04-20 12:45:44 +0200 |
commit | f7fc6fd54a5f750de8144b9b05d5ac173470c70a (patch) | |
tree | 8796fc49df1d9d80c7b8817a54580c98c2f100f5 /src | |
parent | 3983548e7c0f107fa7b7cc3c4c36aa009590b481 (diff) |
Adds Connection wrapper for SSH2
Diffstat (limited to 'src')
-rw-r--r-- | src/Command/RunCommand.php | 29 | ||||
-rw-r--r-- | src/Connection.php | 78 | ||||
-rw-r--r-- | src/Module/Module.php | 14 | ||||
-rw-r--r-- | src/Module/State.php | 2 | ||||
-rw-r--r-- | src/Support/SingletonTraitWithArguments.php | 25 |
5 files changed, 133 insertions, 15 deletions
diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 6d8d2c9..cc9c7fd 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -2,9 +2,8 @@ namespace PHPIAC\Command; +use PHPIAC\Connection; use PHPIAC\Task; -use phpseclib3\Crypt\PublicKeyLoader; -use phpseclib3\Net\SSH2; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -14,36 +13,38 @@ class RunCommand extends Command { protected static $defaultName = 'run'; + /** + * @inheritDoc + */ protected function configure() { $this->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to config file', getcwd() . '/config.php'); } + /** + * @inheritDoc + */ protected function execute(InputInterface $input, OutputInterface $output): int { $config = include $input->getOption('config'); - global $ssh; - $ssh = new SSH2($config['host']); - if (! $ssh->login($config['user'], PublicKeyLoader::load(file_get_contents($config['private_key_file'])))) { - throw new \Exception('Login failed'); - } + Connection::getInstance($config['host'], $config['user'], $config['private_key_file']); - $commands = []; foreach ($config['tasks'] as $task) { /**@var Task $task*/ + if (! $task->module->checkState()) { - $output->writeln($task->getName() . ': Adding commands from ' . get_class($task->module)); - array_push($commands, ...$task->module->getCommands()); + $output->writeln($task->getName()); + $output->writeln('Running'); + + $task->module->getCommands(); } else { - $output->writeln($task->getName() . ': Skipping commands from ' . get_class($task->module)); + $output->writeln($task->getName()); + $output->writeln('Skipping'); } } - // run commands in single exec call - $ssh->exec(implode(PHP_EOL, $commands)); - return Command::SUCCESS; } } diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..2052cd1 --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,78 @@ +<?php + +namespace PHPIAC; + +use PHPIAC\Support\SingletonTraitWithArguments; +use phpseclib3\Crypt\PublicKeyLoader; +use phpseclib3\Net\SFTP; +use phpseclib3\Net\SSH2; + +/** + * Class Connection + * + * @method static string exec(string $command, callback $callback = null) + * @method static string|bool|null read(string $expect = '', int $read = 1) + * @method static enablePty() + * @method static disablePty() + */ +class Connection +{ + use SingletonTraitWithArguments; + + private SSH2 $ssh; + private SFTP $sftp; + + /** + * Connection constructor. + * + * @param string $host + * @param string $user + * @param string $key + * + * @throws \Exception + */ + public function __construct(string $host, string $user, string $key) + { + $this->ssh = new SSH2($host); + $key = PublicKeyLoader::load(file_get_contents($key)); + if (! $this->ssh->login($user, $key)) { + throw new \Exception('SSH Login failed'); + } + + $this->sftp = new SFTP($host); + if (! $this->sftp->login($user, $key)) { + throw new \Exception('SFTP Login failed'); + } + } + + /** + * Calls SSH2 methods statically + * + * @param string $name + * @param array $arguments + * + * @return mixed + */ + public static function __callStatic(string $name, array $arguments): mixed + { + $self = self::getInstance(); + + if (! method_exists($self->ssh, $name)) { + return $self->sftp->$name(...$arguments); + } + + return $self->ssh->$name(...$arguments); + } + + /** + * @see SFTP::put + */ + public static function put($remote_file, $data, $mode = SFTP::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null): bool + { + $tmp = bin2hex(random_bytes(10)); # work around sftp sudo put restrictions + + return + self::getInstance()->sftp->put("/tmp/$tmp", $data, $mode, $start, $local_start, $progressCallback) && + self::getInstance()->ssh->exec("sudo mv /tmp/$tmp $remote_file"); + } +} diff --git a/src/Module/Module.php b/src/Module/Module.php index 51672f9..48ba5f9 100644 --- a/src/Module/Module.php +++ b/src/Module/Module.php @@ -3,4 +3,16 @@ namespace PHPIAC\Module; abstract class Module implements ModuleInterface -{} +{ + /** + * Module constructor. + * + * @param array $config + */ + public function __construct(array $config) + { + foreach ($config as $key => $value) { + $this->$key = $value; + } + } +} diff --git a/src/Module/State.php b/src/Module/State.php index c7f7af9..040b91e 100644 --- a/src/Module/State.php +++ b/src/Module/State.php @@ -6,4 +6,6 @@ class State { public const PRESENT = 'present'; public const ABSENT = 'absent'; + public const ENABLED = 'enabled'; + public const DISABLED = 'disabled'; } diff --git a/src/Support/SingletonTraitWithArguments.php b/src/Support/SingletonTraitWithArguments.php new file mode 100644 index 0000000..2ac0653 --- /dev/null +++ b/src/Support/SingletonTraitWithArguments.php @@ -0,0 +1,25 @@ +<?php + +namespace PHPIAC\Support; + +use PetrKnap\Php\Singleton\SingletonTrait; + +trait SingletonTraitWithArguments +{ + use SingletonTrait; + + /** + * @param mixed ...$arguments + * + * @return self + */ + public static function getInstance(mixed ...$arguments) + { + $self = get_called_class(); + if (! isset(self::$instances[$self])) { + self::$instances[$self] = new $self(...$arguments); + } + + return self::$instances[$self]; + } +} |