summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2021-04-20 12:45:44 +0200
committerDaniel Weipert <code@drogueronin.de>2021-04-20 12:45:44 +0200
commitf7fc6fd54a5f750de8144b9b05d5ac173470c70a (patch)
tree8796fc49df1d9d80c7b8817a54580c98c2f100f5
parent3983548e7c0f107fa7b7cc3c4c36aa009590b481 (diff)
Adds Connection wrapper for SSH2
-rw-r--r--composer.lock152
-rw-r--r--src/Command/RunCommand.php29
-rw-r--r--src/Connection.php78
-rw-r--r--src/Module/Module.php14
-rw-r--r--src/Module/State.php2
-rw-r--r--src/Support/SingletonTraitWithArguments.php25
6 files changed, 248 insertions, 52 deletions
diff --git a/composer.lock b/composer.lock
index d26cd7d..6f79623 100644
--- a/composer.lock
+++ b/composer.lock
@@ -177,7 +177,8 @@
"reference": "d4d5ae3bb6566311b2d42cf888e463b62f6cf0dc"
},
"require": {
- "php": "^8.0"
+ "php": "^8.0",
+ "twig/twig": "^3.0"
},
"require-dev": {
"php-iac/php-iac": "*"
@@ -561,12 +562,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
+ "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
- "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
+ "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"shasum": ""
},
"require": {
@@ -579,7 +580,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -617,7 +618,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/main"
},
"funding": [
{
@@ -633,7 +634,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-07T16:49:33+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@@ -641,12 +642,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
- "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170"
+ "reference": "053f7184175d5417c933817341c5cc0053ddacd5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170",
- "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/053f7184175d5417c933817341c5cc0053ddacd5",
+ "reference": "053f7184175d5417c933817341c5cc0053ddacd5",
"shasum": ""
},
"require": {
@@ -659,7 +660,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -699,7 +700,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/main"
},
"funding": [
{
@@ -715,7 +716,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-22T09:19:47+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
@@ -723,12 +724,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
- "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
"shasum": ""
},
"require": {
@@ -741,7 +742,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -784,7 +785,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/main"
},
"funding": [
{
@@ -800,7 +801,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-22T09:19:47+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -808,12 +809,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+ "reference": "298b87cbbe99cb2c9f88fb1d1de78833b64b483e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
- "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/298b87cbbe99cb2c9f88fb1d1de78833b64b483e",
+ "reference": "298b87cbbe99cb2c9f88fb1d1de78833b64b483e",
"shasum": ""
},
"require": {
@@ -826,7 +827,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -865,7 +866,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/main"
},
"funding": [
{
@@ -881,7 +882,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-22T09:19:47+00:00"
+ "time": "2021-04-19T09:32:22+00:00"
},
{
"name": "symfony/polyfill-php73",
@@ -889,12 +890,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
- "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
"shasum": ""
},
"require": {
@@ -904,7 +905,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -945,7 +946,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-php73/tree/main"
},
"funding": [
{
@@ -961,7 +962,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-07T16:49:33+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-php80",
@@ -969,12 +970,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+ "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
- "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
+ "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
"shasum": ""
},
"require": {
@@ -984,7 +985,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.22-dev"
+ "dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1029,7 +1030,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+ "source": "https://github.com/symfony/polyfill-php80/tree/main"
},
"funding": [
{
@@ -1045,7 +1046,7 @@
"type": "tidelift"
}
],
- "time": "2021-01-07T16:49:33+00:00"
+ "time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/service-contracts",
@@ -1210,6 +1211,83 @@
}
],
"time": "2021-03-17T17:12:23+00:00"
+ },
+ {
+ "name": "twig/twig",
+ "version": "3.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twigphp/Twig.git",
+ "reference": "32d72de1885889452f3d8aa298a90b04d263651b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/32d72de1885889452f3d8aa298a90b04d263651b",
+ "reference": "32d72de1885889452f3d8aa298a90b04d263651b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-ctype": "^1.8",
+ "symfony/polyfill-mbstring": "^1.3"
+ },
+ "require-dev": {
+ "psr/container": "^1.0",
+ "symfony/phpunit-bridge": "^4.4.9|^5.0.9"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Twig\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com",
+ "homepage": "http://fabien.potencier.org",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Twig Team",
+ "role": "Contributors"
+ },
+ {
+ "name": "Armin Ronacher",
+ "email": "armin.ronacher@active-4.com",
+ "role": "Project Founder"
+ }
+ ],
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
+ "homepage": "https://twig.symfony.com",
+ "keywords": [
+ "templating"
+ ],
+ "support": {
+ "issues": "https://github.com/twigphp/Twig/issues",
+ "source": "https://github.com/twigphp/Twig/tree/3.x"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/twig/twig",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-04-15T10:09:30+00:00"
}
],
"packages-dev": [],
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];
+ }
+}