Skip to content

Commit

Permalink
Feat - Add support of Process concurrently and pool
Browse files Browse the repository at this point in the history
  • Loading branch information
lguichard authored Jan 4, 2025
2 parents e3eeb08 + a37e3c3 commit ba551af
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 89 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,46 @@ $result = Process::ssh([
->run('ls -al');
```

### Use the favorites method provided by Laravel's Process class.

For more information, refer to the official documentation : https://laravel.com/docs/11.x/processes

```php
$process = Process::ssh([
'host' => '192.168.1.10',
'user' => 'username',
'password' => 'your_password',
])
->start('bash import.sh');

$result = $process->wait();

```

```php
[$result1, $result2] = Process::ssh([
'host' => '192.168.1.10',
'user' => 'username',
'password' => 'your_password',
])
->concurrently(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});
```

```php
$result = Process::ssh([
'host' => '192.168.1.10',
'user' => 'username',
'password' => 'your_password',
])
->pool(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});
```

### SSH multiplexing

If you want to execute multiple commands over the same SSH connection, SSH multiplexing allows you to reuse an existing TCP connection, improving efficiency and reducing overhead.
Expand Down
20 changes: 19 additions & 1 deletion src/PendingProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ class PendingProcess extends BasePendingProcess

private bool $handleSsh = false;

/**
* Override the command string to handle SSH commands.
*/
public function command(array|string $command)
{
$command = $this->buildCommand($command);

$this->command = $command;

return $this;
}

/**
* Set configuration for the SSH connection.
*/
Expand Down Expand Up @@ -60,6 +72,10 @@ public function setConfig(array $config, bool $handleSsh)
*/
public function buildCommand(array|string|null $command): array|string|null
{
if (! $command) {
return $command;
}

if (! $this->handleSsh) {
return $command;
}
Expand Down Expand Up @@ -161,6 +177,8 @@ protected function toSymfonyProcess(array|string|null $command)
{
$command = $this->buildCommand($command);

return parent::toSymfonyProcess($command);
$process = parent::toSymfonyProcess($command);

return $process;
}
}
19 changes: 0 additions & 19 deletions src/ProcessSsh.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,6 @@ public function addExtraOption(string $option): self
return $this;
}

public function pool(callable $callback)
{
if ($this->handleSsh) {
throw new \InvalidArgumentException('Cannot pool processes with SSH enabled.');
}

return parent::pool($callback);
}

public function pipe(callable|array $callback, ?callable $output = null)
{
if ($this->handleSsh) {
Expand All @@ -79,16 +70,6 @@ public function pipe(callable|array $callback, ?callable $output = null)
return parent::pipe($callback, $output);
}

public function concurrently(callable $callback, ?callable $output = null)
{

if ($this->handleSsh) {
throw new \InvalidArgumentException('Cannot concurrently processes with SSH enabled.');
}

return parent::concurrently($callback, $output);
}

/**
* Create a new pending process instance.
*/
Expand Down
154 changes: 85 additions & 69 deletions tests/ProcessSshTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@
use Bagel\ProcessSsh\PendingProcess;
use Bagel\ProcessSsh\Providers\ProcessSshServiceProvider;
use Illuminate\Process\FakeProcessResult;
use Illuminate\Process\Pool;
use Illuminate\Support\Facades\Process;
use Orchestra\Testbench\TestCase;

uses(TestCase::class)->beforeEach(function () {
$this->app->register(ProcessSshServiceProvider::class);

$this->basicSshConfig = [
'host' => '192.178.0.1',
'user' => 'ubuntu',
'port' => 22,
];
});

it('process config set', function () {
$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'port' => 22,
'extraOptions' => [
Expand All @@ -22,7 +29,7 @@
'private_key' => '/path/to/key',
]);

expect($process->sshConfig()['host'])->toBe('example.com');
expect($process->sshConfig()['host'])->toBe('192.178.0.1');
expect($process->sshConfig()['user'])->toBe('ubuntu');
expect($process->sshConfig()['port'])->toBe(22);
expect($process->sshConfig()['extraOptions'])->toBe([
Expand All @@ -34,7 +41,7 @@

it('process add extra options', function () {
$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'port' => 22,
])->addExtraOption('-o StrictHostKeyChecking=no');
Expand All @@ -46,7 +53,7 @@

it('process disableStrictHostKeyChecking', function () {
$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'port' => 22,
])->disableStrictHostKeyChecking();
Expand Down Expand Up @@ -87,12 +94,12 @@
Process::fake();

$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
])
->disableStrictHostKeyChecking()
->run('ls -al');

expect($process->command())->toBe("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null example.com 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');
expect($process->command())->toBe("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null 192.178.0.1 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');

Process::assertRan('ls -al');
});
Expand All @@ -101,7 +108,7 @@
Process::fake();

$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
Expand All @@ -113,7 +120,7 @@
->disableStrictHostKeyChecking()
->run('ls -al');

expect($process->command())->toBe("sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@example.com 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');
expect($process->command())->toBe("sshpass -p 'password' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@192.178.0.1 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');

Process::assertRan('ls -al');
});
Expand All @@ -122,37 +129,19 @@
Process::fake();

$process = Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'port' => 22,
'private_key' => '/path/to/key',
])
->disableStrictHostKeyChecking()
->run('ls -al');

expect($process->command())->toBe("ssh -i /path/to/key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@example.com 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');
expect($process->command())->toBe("ssh -i /path/to/key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@192.178.0.1 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');

Process::assertRan('ls -al');
});

it('exception thrown process run with array', function () {
Process::fake();

Process::ssh([
'host' => '127.0.0.1',
])->run(['ls', '-al']);

})->throws(InvalidArgumentException::class, 'Array commands are not supported for SSH connections');

it('exception thrown process start with array', function () {
Process::fake();

Process::ssh([
'host' => '127.0.0.1',
])->start(['ls', '-al']);

})->throws(InvalidArgumentException::class, 'Array commands are not supported for SSH connections');

it('Process can run normaly', function () {
Process::fake();

Expand Down Expand Up @@ -183,7 +172,7 @@
Process::fake();

Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
Expand All @@ -202,7 +191,7 @@
Process::fake();

Process::ssh([
'host' => 'example.com',
'host' => '192.178.0.1',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
Expand All @@ -217,60 +206,87 @@
});
});

it('can\'t pool processes with SSH enabled', function () {
it('invoke process::concurrently', function () {
Process::fake();

Process::ssh([
'host' => 'example.com',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
])->pool(function () {
//
$process = Process::concurrently(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});

})->throws(InvalidArgumentException::class, 'Cannot pool processes with SSH enabled.');
expect($process[0]->command())->toBe('ls -al');
expect($process[1]->command())->toBe('whoami');

Process::assertRan('ls -al');
Process::assertRan('whoami');
});

it('can\'t pipe processes with SSH enabled', function () {
it('invoke process::concurrently ssh', function () {
Process::fake();

Process::ssh([
'host' => 'example.com',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
])->pipe(function () {
//
$process = Process::ssh($this->basicSshConfig)->concurrently(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});

})->throws(InvalidArgumentException::class, 'Cannot pipe processes with SSH enabled.');
expect($process[0]->command())->toBe("ssh [email protected] 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');
expect($process[1]->command())->toBe("ssh [email protected] 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'whoami'.PHP_EOL.'EOF-PROCESS-SSH');

});

it('can\'t concurrently processes with SSH enabled', function () {
it('invoke process::pool', function () {
Process::fake();

Process::ssh([
'host' => 'example.com',
'user' => 'ubuntu',
'password' => 'password',
'port' => 22,
])->concurrently(function () {
//
$process = Process::pool(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});

$results = $process->wait();
expect($results[0]->command())->toBe('ls -al');
expect($results[1]->command())->toBe('whoami');

Process::assertRan('ls -al');
Process::assertRan('whoami');
});

it('invoke process::pool ssh', function () {
Process::fake();

$process = Process::ssh($this->basicSshConfig)->pool(function (Pool $pool) {
$pool->command('ls -al');
$pool->command('whoami');
});

})->throws(InvalidArgumentException::class, 'Cannot concurrently processes with SSH enabled.');
$results = $process->wait();
expect($results[0]->command())->toBe("ssh [email protected] 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'ls -al'.PHP_EOL.'EOF-PROCESS-SSH');
expect($results[1]->command())->toBe("ssh [email protected] 'bash -se' << \EOF-PROCESS-SSH".PHP_EOL.'whoami'.PHP_EOL.'EOF-PROCESS-SSH');

});

it('exception thrown process run with array', function () {
Process::fake();

Process::ssh([
'host' => '127.0.0.1',
])->run(['ls', '-al']);

})->throws(InvalidArgumentException::class, 'Array commands are not supported for SSH connections.');

it('exception thrown process start with array', function () {
Process::fake();

// it('timeout process', function () {
// //Process::fake();
Process::ssh([
'host' => '127.0.0.1',
])->start(['ls', '-al']);

// $process = Process::ssh([
// 'host' => '192.168.8.5',
// 'user' => 'ubuntu',
// //'password' => 'password',
// 'port' => 22,
// ])
// ->run('ls');
})->throws(InvalidArgumentException::class, 'Array commands are not supported for SSH connections.');

// dump($process->errorOutput());
// dump($process->output());
it('invoke process::pipe', function () {
Process::fake();

// })->only();
$process = Process::ssh($this->basicSshConfig)->pipe([
'ls -al',
'whoami',
]);
})->throws(InvalidArgumentException::class, 'Cannot pipe processes with SSH enabled.');

0 comments on commit ba551af

Please sign in to comment.