From 0d21e1b3f5244de77f5bf3b9283439f906c35e6b Mon Sep 17 00:00:00 2001 From: Tom Shafer Date: Fri, 21 Aug 2015 00:07:21 -0400 Subject: [PATCH] Applied fixes from StyleCI --- config/remote.php | 95 ++--- phpunit.php | 46 +-- src/Connection.php | 526 ++++++++++++++-------------- src/ConnectionInterface.php | 98 +++--- src/Console/TailCommand.php | 105 +++--- src/GatewayInterface.php | 126 +++---- src/MultiConnection.php | 172 ++++----- src/RemoteFacade.php | 27 +- src/RemoteManager.php | 385 +++++++++++---------- src/RemoteServiceProvider.php | 158 ++++----- src/SecLibGateway.php | 631 ++++++++++++++++++---------------- tests/RemoteSecLibTest.php | 95 ++--- 12 files changed, 1272 insertions(+), 1192 deletions(-) diff --git a/config/remote.php b/config/remote.php index 958498d..fe54a92 100644 --- a/config/remote.php +++ b/config/remote.php @@ -1,48 +1,49 @@ 'production', - /* - |-------------------------------------------------------------------------- - | Remote Server Connections - |-------------------------------------------------------------------------- - | - | These are the servers that will be accessible via the SSH task runner - | facilities of Laravel. This feature radically simplifies executing - | tasks on your servers, such as deploying out these applications. - | - */ - 'connections' => [ - 'production' => [ - 'host' => '', - 'username' => '', - 'password' => '', - 'key' => '', - 'keytext' => '', - 'keyphrase' => '', - 'agent' => '', - ], - ], - /* - |-------------------------------------------------------------------------- - | Remote Server Groups - |-------------------------------------------------------------------------- - | - | Here you may list connections under a single group name, which allows - | you to easily access all of the servers at once using a short name - | that is extremely easy to remember, such as "web" or "database". - | - */ - 'groups' => [ - 'web' => [ 'production' ] - ], - ]; + + return [ + /* + |-------------------------------------------------------------------------- + | Default Remote Connection Name + |-------------------------------------------------------------------------- + | + | Here you may specify the default connection that will be used for SSH + | operations. This name should correspond to a connection name below + | in the server list. Each connection will be manually accessible. + | + */ + 'default' => 'production', + /* + |-------------------------------------------------------------------------- + | Remote Server Connections + |-------------------------------------------------------------------------- + | + | These are the servers that will be accessible via the SSH task runner + | facilities of Laravel. This feature radically simplifies executing + | tasks on your servers, such as deploying out these applications. + | + */ + 'connections' => [ + 'production' => [ + 'host' => '', + 'username' => '', + 'password' => '', + 'key' => '', + 'keytext' => '', + 'keyphrase' => '', + 'agent' => '', + ], + ], + /* + |-------------------------------------------------------------------------- + | Remote Server Groups + |-------------------------------------------------------------------------- + | + | Here you may list connections under a single group name, which allows + | you to easily access all of the servers at once using a short name + | that is extremely easy to remember, such as "web" or "database". + | + */ + 'groups' => [ + 'web' => ['production'], + ], + ]; diff --git a/phpunit.php b/phpunit.php index 923e82d..656569a 100644 --- a/phpunit.php +++ b/phpunit.php @@ -1,24 +1,24 @@ name = $name; - $this->host = $host; - $this->username = $username; - $this->gateway = $gateway ?: new SecLibGateway( $host, $auth, new Filesystem ); - } - - /** - * Define a set of commands as a task. - * - * @param string $task - * @param string|array $commands - * - * @return void - */ - public function define( $task, $commands ) { - $this->tasks[ $task ] = $commands; - } - - /** - * Run a task against the connection. - * - * @param string $task - * @param \Closure $callback - * - * @return void - */ - public function task( $task, Closure $callback = null ) { - if ( isset( $this->tasks[ $task ] ) ) { - return $this->run( $this->tasks[ $task ], $callback ); - } - } - - /** - * Run a set of commands against the connection. - * - * @param string|array $commands - * @param \Closure $callback - * - * @return void - */ - public function run( $commands, Closure $callback = null ) { - // First, we will initialize the SSH gateway, and then format the commands so - // they can be run. Once we have the commands formatted and the server is - // ready to go we will just fire off these commands against the server. - $gateway = $this->getGateway(); - - $callback = $this->getCallback( $callback ); - - $gateway->run( $this->formatCommands( $commands ) ); - - // After running the commands against the server, we will continue to ask for - // the next line of output that is available, and write it them out using - // our callback. Once we hit the end of output, we'll bail out of here. - while( true ) { - if ( is_null( $line = $gateway->nextLine() ) ) { - break; - } - - call_user_func( $callback, $line, $this ); - } - } - - /** - * Download the contents of a remote file. - * - * @param string $remote - * @param string $local - * - * @return void - */ - public function get( $remote, $local ) { - $this->getGateway()->get( $remote, $local ); - } - - /** - * Get the contents of a remote file. - * - * @param string $remote - * - * @return string - */ - public function getString( $remote ) { - return $this->getGateway()->getString( $remote ); - } - - /** - * Upload a local file to the server. - * - * @param string $local - * @param string $remote - * - * @return void - */ - public function put( $local, $remote ) { - $this->getGateway()->put( $local, $remote ); - } - - /** - * Upload a string to to the given file on the server. - * - * @param string $remote - * @param string $contents - * - * @return void - */ - public function putString( $remote, $contents ) { - $this->getGateway()->putString( $remote, $contents ); - } - - /** - * Display the given line using the default output. - * - * @param string $line - * - * @return void - */ - public function display( $line ) { - $server = $this->username . '@' . $this->host; - - $lead = '[' . $server . '] (' . $this->name . ')'; - - $this->getOutput()->writeln( $lead . ' ' . $line ); - } - - /** - * Format the given command set. - * - * @param string|array $commands - * - * @return string - */ - protected function formatCommands( $commands ) { - return is_array( $commands ) ? implode( ' && ', $commands ) : $commands; - } - - /** - * Get the display callback for the connection. - * - * @param \Closure|null $callback - * - * @return \Closure - */ - protected function getCallback( $callback ) { - if ( ! is_null( $callback ) ) { - return $callback; - } - - return function ( $line ) { - $this->display( $line ); - }; - } - - /** - * Get the exit status of the last command. - * - * @return int|bool - */ - public function status() { - return $this->gateway->status(); - } - - /** - * Get the gateway implementation. - * - * @return \Illuminate\Remote\GatewayInterface - * @throws \RuntimeException - */ - public function getGateway() { - if ( ! $this->gateway->connected() && ! $this->gateway->connect( $this->username ) ) { - throw new \RuntimeException( "Unable to connect to remote server." ); - } - - return $this->gateway; - } - - /** - * Get the output implementation for the connection. - * - * @return \Symfony\Component\Console\Output\OutputInterface - */ - public function getOutput() { - if ( is_null( $this->output ) ) { - $this->output = new NullOutput; - } - - return $this->output; - } - - /** - * Set the output implementation. - * - * @param \Symfony\Component\Console\Output\OutputInterface $output - * - * @return void - */ - public function setOutput( OutputInterface $output ) { - $this->output = $output; - } +class Connection implements ConnectionInterface +{ + /** + * The SSH gateway implementation. + * + * @var \Collective\Remote\GatewayInterface + */ + protected $gateway; + + /** + * The name of the connection. + * + * @var string + */ + protected $name; + + /** + * The host name of the server. + * + * @var string + */ + protected $host; + + /** + * The username for the connection. + * + * @var string + */ + protected $username; + + /** + * All of the defined tasks. + * + * @var array + */ + protected $tasks = []; + + /** + * The output implementation for the connection. + * + * @var \Symfony\Component\Console\Output\OutputInterface + */ + protected $output; + + /** + * Create a new SSH connection instance. + * + * @param string $name + * @param string $host + * @param string $username + * @param array $auth + * @param \Collective\Remote\GatewayInterface + * @param + */ + public function __construct($name, $host, $username, array $auth, GatewayInterface $gateway = null) + { + $this->name = $name; + $this->host = $host; + $this->username = $username; + $this->gateway = $gateway ?: new SecLibGateway($host, $auth, new Filesystem()); + } + + /** + * Define a set of commands as a task. + * + * @param string $task + * @param string|array $commands + * + * @return void + */ + public function define($task, $commands) + { + $this->tasks[ $task ] = $commands; + } + + /** + * Run a task against the connection. + * + * @param string $task + * @param \Closure $callback + * + * @return void + */ + public function task($task, Closure $callback = null) + { + if (isset($this->tasks[ $task ])) { + return $this->run($this->tasks[ $task ], $callback); + } + } + + /** + * Run a set of commands against the connection. + * + * @param string|array $commands + * @param \Closure $callback + * + * @return void + */ + public function run($commands, Closure $callback = null) + { + // First, we will initialize the SSH gateway, and then format the commands so + // they can be run. Once we have the commands formatted and the server is + // ready to go we will just fire off these commands against the server. + $gateway = $this->getGateway(); + + $callback = $this->getCallback($callback); + + $gateway->run($this->formatCommands($commands)); + + // After running the commands against the server, we will continue to ask for + // the next line of output that is available, and write it them out using + // our callback. Once we hit the end of output, we'll bail out of here. + while (true) { + if (is_null($line = $gateway->nextLine())) { + break; + } + + call_user_func($callback, $line, $this); + } + } + + /** + * Download the contents of a remote file. + * + * @param string $remote + * @param string $local + * + * @return void + */ + public function get($remote, $local) + { + $this->getGateway()->get($remote, $local); + } + + /** + * Get the contents of a remote file. + * + * @param string $remote + * + * @return string + */ + public function getString($remote) + { + return $this->getGateway()->getString($remote); + } + + /** + * Upload a local file to the server. + * + * @param string $local + * @param string $remote + * + * @return void + */ + public function put($local, $remote) + { + $this->getGateway()->put($local, $remote); + } + + /** + * Upload a string to to the given file on the server. + * + * @param string $remote + * @param string $contents + * + * @return void + */ + public function putString($remote, $contents) + { + $this->getGateway()->putString($remote, $contents); + } + + /** + * Display the given line using the default output. + * + * @param string $line + * + * @return void + */ + public function display($line) + { + $server = $this->username.'@'.$this->host; + + $lead = '['.$server.'] ('.$this->name.')'; + + $this->getOutput()->writeln($lead.' '.$line); + } + + /** + * Format the given command set. + * + * @param string|array $commands + * + * @return string + */ + protected function formatCommands($commands) + { + return is_array($commands) ? implode(' && ', $commands) : $commands; + } + + /** + * Get the display callback for the connection. + * + * @param \Closure|null $callback + * + * @return \Closure + */ + protected function getCallback($callback) + { + if (!is_null($callback)) { + return $callback; + } + + return function ($line) { + $this->display($line); + }; + } + + /** + * Get the exit status of the last command. + * + * @return int|bool + */ + public function status() + { + return $this->gateway->status(); + } + + /** + * Get the gateway implementation. + * + * @throws \RuntimeException + * + * @return \Illuminate\Remote\GatewayInterface + */ + public function getGateway() + { + if (!$this->gateway->connected() && !$this->gateway->connect($this->username)) { + throw new \RuntimeException('Unable to connect to remote server.'); + } + + return $this->gateway; + } + + /** + * Get the output implementation for the connection. + * + * @return \Symfony\Component\Console\Output\OutputInterface + */ + public function getOutput() + { + if (is_null($this->output)) { + $this->output = new NullOutput(); + } + + return $this->output; + } + + /** + * Set the output implementation. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return void + */ + public function setOutput(OutputInterface $output) + { + $this->output = $output; + } } diff --git a/src/ConnectionInterface.php b/src/ConnectionInterface.php index 94494bd..93e2b5d 100755 --- a/src/ConnectionInterface.php +++ b/src/ConnectionInterface.php @@ -1,56 +1,58 @@ -getPath( $this->argument( 'connection' ) ); + $path = $this->getPath($this->argument('connection')); if ($path) { - $this->tailLogFile( $path, $this->argument( 'connection' ) ); + $this->tailLogFile($path, $this->argument('connection')); } else { - $this->error( 'Could not determine path to log file.' ); + $this->error('Could not determine path to log file.'); } } /** * Tail the given log file for the connection. * - * @param string $path - * @param string $connection + * @param string $path + * @param string $connection * * @return void */ - protected function tailLogFile( $path, $connection ) + protected function tailLogFile($path, $connection) { - if (is_null( $connection )) { - $this->tailLocalLogs( $path ); + if (is_null($connection)) { + $this->tailLocalLogs($path); } else { - $this->tailRemoteLogs( $path, $connection ); + $this->tailRemoteLogs($path, $connection); } } /** * Tail a local log file for the application. * - * @param string $path + * @param string $path * * @return string */ - protected function tailLocalLogs( $path ) + protected function tailLocalLogs($path) { - $path = $this->findNewestLocalLogfile( $path ); + $path = $this->findNewestLocalLogfile($path); $output = $this->output; - $lines = $this->option( 'lines' ); + $lines = $this->option('lines'); - ( new Process( 'tail -f -n ' . $lines . ' ' . escapeshellarg( $path ) ) )->setTimeout( null )->run( function ( $type, $line ) use ( $output ) { - $output->write( $line ); - } ); + ( new Process('tail -f -n '.$lines.' '.escapeshellarg($path)) )->setTimeout(null)->run(function ($type, $line) use ($output) { + $output->write($line); + }); return $path; } @@ -80,25 +81,22 @@ protected function tailLocalLogs( $path ) /** * Tail a remote log file at the given path and connection. * - * @param string $path - * @param string $connection + * @param string $path + * @param string $connection * * @return void */ - protected function tailRemoteLogs( $path, $connection ) + protected function tailRemoteLogs($path, $connection) { - $out = $this->output; - $lines = $this->option( 'lines' ); - - $this->getRemote( $connection )->run( 'cd ' . escapeshellarg( $path ) . ' && tail -f $(ls -t | head -n 1) -n ' . $lines , function ( $line ) use ( $out ) { - $out->write( $line ); - } ); + $lines = $this->option('lines'); + $this->getRemote($connection)->run('cd '.escapeshellarg($path).' && tail -f $(ls -t | head -n 1) -n '.$lines, function ($line) use ($out) { + $out->write($line); + }); } - /** * Get the path to the latest local Laravel log file. * @@ -106,16 +104,15 @@ protected function tailRemoteLogs( $path, $connection ) * * @return mixed */ - protected function findNewestLocalLogfile( $path ) + protected function findNewestLocalLogfile($path) { + $files = glob($path.'/*.log'); - $files = glob( $path.'/*.log'); - - $files = array_combine( $files, array_map( "filemtime", $files ) ); + $files = array_combine($files, array_map('filemtime', $files)); - arsort( $files ); + arsort($files); - $newestLogFile = key( $files ); + $newestLogFile = key($files); return $newestLogFile; } @@ -123,47 +120,47 @@ protected function findNewestLocalLogfile( $path ) /** * Get a connection to the remote server. * - * @param string $connection + * @param string $connection * * @return \Collective\Remote\Connection */ - protected function getRemote( $connection ) + protected function getRemote($connection) { - return $this->laravel[ 'remote' ]->connection( $connection ); + return $this->laravel[ 'remote' ]->connection($connection); } /** * Get the path to the Laravel log file. * - * @param string $connection + * @param string $connection * * @return string */ - protected function getPath( $connection ) + protected function getPath($connection) { - if ($this->option( 'path' )) { - return $this->option( 'path' ); + if ($this->option('path')) { + return $this->option('path'); } - if (is_null( $connection ) && $this->option( 'path' )) { - return storage_path( $this->option( 'path' ) ); - } elseif (is_null( $connection ) && ! $this->option( 'path' )) { - return storage_path( 'logs' ); + if (is_null($connection) && $this->option('path')) { + return storage_path($this->option('path')); + } elseif (is_null($connection) && !$this->option('path')) { + return storage_path('logs'); } - return $this->getRoot( $connection ) . str_replace( base_path(), '', storage_path( 'logs' ) ); + return $this->getRoot($connection).str_replace(base_path(), '', storage_path('logs')); } /** * Get the path to the Laravel install root. * - * @param string $connection + * @param string $connection * * @return string */ - protected function getRoot( $connection ) + protected function getRoot($connection) { - return $this->laravel[ 'config' ][ 'remote.connections.' . $connection . '.root' ]; + return $this->laravel[ 'config' ][ 'remote.connections.'.$connection.'.root' ]; } /** @@ -174,7 +171,7 @@ protected function getRoot( $connection ) protected function getArguments() { return [ - [ 'connection', InputArgument::OPTIONAL, 'The remote connection name' ], + ['connection', InputArgument::OPTIONAL, 'The remote connection name'], ]; } @@ -186,8 +183,8 @@ protected function getArguments() protected function getOptions() { return [ - [ 'path', null, InputOption::VALUE_OPTIONAL, 'The fully qualified path to the log file.' ], - [ 'lines', null, InputOption::VALUE_OPTIONAL, 'The number of lines to tail.', 20 ], + ['path', null, InputOption::VALUE_OPTIONAL, 'The fully qualified path to the log file.'], + ['lines', null, InputOption::VALUE_OPTIONAL, 'The number of lines to tail.', 20], ]; } -} \ No newline at end of file +} diff --git a/src/GatewayInterface.php b/src/GatewayInterface.php index f6fbdf1..6bf4304 100755 --- a/src/GatewayInterface.php +++ b/src/GatewayInterface.php @@ -1,63 +1,65 @@ -connections = $connections; - } + /** + * The array of connections. + * + * @param array $connections + */ + public function __construct(array $connections) + { + $this->connections = $connections; + } - /** - * Define a set of commands as a task. - * - * @param string $task - * @param string|array $commands - * - * @return void - */ - public function define( $task, $commands ) { - foreach ( $this->connections as $connection ) { - $connection->define( $task, $commands ); - } - } + /** + * Define a set of commands as a task. + * + * @param string $task + * @param string|array $commands + * + * @return void + */ + public function define($task, $commands) + { + foreach ($this->connections as $connection) { + $connection->define($task, $commands); + } + } - /** - * Run a task against the connection. - * - * @param string $task - * @param \Closure $callback - * - * @return void - */ - public function task( $task, Closure $callback = null ) { - foreach ( $this->connections as $connection ) { - $connection->task( $task, $callback ); - } - } + /** + * Run a task against the connection. + * + * @param string $task + * @param \Closure $callback + * + * @return void + */ + public function task($task, Closure $callback = null) + { + foreach ($this->connections as $connection) { + $connection->task($task, $callback); + } + } - /** - * Run a set of commands against the connection. - * - * @param string|array $commands - * @param \Closure $callback - * - * @return void - */ - public function run( $commands, Closure $callback = null ) { - foreach ( $this->connections as $connection ) { - $connection->run( $commands, $callback ); - } - } + /** + * Run a set of commands against the connection. + * + * @param string|array $commands + * @param \Closure $callback + * + * @return void + */ + public function run($commands, Closure $callback = null) + { + foreach ($this->connections as $connection) { + $connection->run($commands, $callback); + } + } - /** - * Upload a local file to the server. - * - * @param string $local - * @param string $remote - * - * @return void - */ - public function put( $local, $remote ) { - foreach ( $this->connections as $connection ) { - $connection->put( $local, $remote ); - } - } + /** + * Upload a local file to the server. + * + * @param string $local + * @param string $remote + * + * @return void + */ + public function put($local, $remote) + { + foreach ($this->connections as $connection) { + $connection->put($local, $remote); + } + } - /** - * Upload a string to to the given file on the server. - * - * @param string $remote - * @param string $contents - * - * @return void - */ - public function putString( $remote, $contents ) { - foreach ( $this->connections as $connection ) { - $connection->putString( $remote, $contents ); - } - } + /** + * Upload a string to to the given file on the server. + * + * @param string $remote + * @param string $contents + * + * @return void + */ + public function putString($remote, $contents) + { + foreach ($this->connections as $connection) { + $connection->putString($remote, $contents); + } + } } diff --git a/src/RemoteFacade.php b/src/RemoteFacade.php index 1163cc7..b3bd6b1 100755 --- a/src/RemoteFacade.php +++ b/src/RemoteFacade.php @@ -1,4 +1,6 @@ -app = $app; - } - - /** - * Get a remote connection instance. - * - * @param string|array|mixed $name - * - * @return \Collective\Remote\ConnectionInterface - */ - public function into( $name ) { - if ( is_string( $name ) || is_array( $name ) ) { - return $this->connection( $name ); - } else { - return $this->connection( func_get_args() ); - } - } - - /** - * Get a remote connection instance. - * - * @param string|array $name - * - * @return \Collective\Remote\ConnectionInterface - */ - public function connection( $name = null ) { - if ( is_array( $name ) ) { - return $this->multiple( $name ); - } - - return $this->resolve( $name ?: $this->getDefaultConnection() ); - } - - /** - * Get a connection group instance by name. - * - * @param string $name - * - * @return \Collective\Remote\ConnectionInterface - */ - public function group( $name ) { - return $this->connection( $this->app['config'][ 'remote.groups.' . $name ] ); - } - - /** - * Resolve a multiple connection instance. - * - * @param array $names - * - * @return \Collective\Remote\MultiConnection - */ - public function multiple( array $names ) { - return new MultiConnection( array_map( [ $this, 'resolve' ], $names ) ); - } - - /** - * Resolve a remote connection instance. - * - * @param string $name - * - * @return \Collective\Remote\Connection - */ - public function resolve( $name ) { - return $this->makeConnection( $name, $this->getConfig( $name ) ); - } - - /** - * Make a new connection instance. - * - * @param string $name - * @param array $config - * - * @return \Collective\Remote\Connection - */ - protected function makeConnection( $name, array $config ) { - $this->setOutput( $connection = new Connection( - - $name, $config['host'], $config['username'], $this->getAuth( $config ) - - ) ); - - return $connection; - } - - /** - * Set the output implementation on the connection. - * - * @param \Collective\Remote\Connection $connection - * - * @return void - */ - protected function setOutput( Connection $connection ) { - $output = php_sapi_name() == 'cli' ? new ConsoleOutput : new NullOutput; - - $connection->setOutput( $output ); - } - - /** - * Format the appropriate authentication array payload. - * - * @param array $config - * - * @return array - * @throws \InvalidArgumentException - */ - protected function getAuth( array $config ) { - if ( isset( $config['agent'] ) && $config['agent'] === true ) { - return [ 'agent' => true ]; - } elseif ( isset( $config['key'] ) && trim( $config['key'] ) != '' ) { - return [ 'key' => $config['key'], 'keyphrase' => $config['keyphrase'] ]; - } elseif ( isset( $config['keytext'] ) && trim( $config['keytext'] ) != '' ) { - return [ 'keytext' => $config['keytext'] ]; - } elseif ( isset( $config['password'] ) ) { - return [ 'password' => $config['password'] ]; - } - - throw new \InvalidArgumentException( 'Password / key is required.' ); - } - - /** - * Get the configuration for a remote server. - * - * @param string $name - * - * @return array - * @throws \InvalidArgumentException - */ - protected function getConfig( $name ) { - $config = $this->app['config'][ 'remote.connections.' . $name ]; - - if ( ! is_null( $config ) ) { - return $config; - } - - throw new \InvalidArgumentException( "Remote connection [$name] not defined." ); - } - - /** - * Get the default connection name. - * - * @return string - */ - public function getDefaultConnection() { - return $this->app['config']['remote.default']; - } - - /** - * Set the default connection name. - * - * @param string $name - * - * @return void - */ - public function setDefaultConnection( $name ) { - $this->app['config']['remote.default'] = $name; - } - - /** - * Dynamically pass methods to the default connection. - * - * @param string $method - * @param array $parameters - * - * @return mixed - */ - public function __call( $method, $parameters ) { - return call_user_func_array( [ $this->connection(), $method ], $parameters ); - } +class RemoteManager +{ + /** + * The application instance. + * + * @var \Collective\Foundation\Application + */ + protected $app; + + /** + * Create a new remote manager instance. + * + * @param \Illuminate\Foundation\Application $app + */ + public function __construct(Application $app) + { + $this->app = $app; + } + + /** + * Get a remote connection instance. + * + * @param string|array|mixed $name + * + * @return \Collective\Remote\ConnectionInterface + */ + public function into($name) + { + if (is_string($name) || is_array($name)) { + return $this->connection($name); + } else { + return $this->connection(func_get_args()); + } + } + + /** + * Get a remote connection instance. + * + * @param string|array $name + * + * @return \Collective\Remote\ConnectionInterface + */ + public function connection($name = null) + { + if (is_array($name)) { + return $this->multiple($name); + } + + return $this->resolve($name ?: $this->getDefaultConnection()); + } + + /** + * Get a connection group instance by name. + * + * @param string $name + * + * @return \Collective\Remote\ConnectionInterface + */ + public function group($name) + { + return $this->connection($this->app['config'][ 'remote.groups.'.$name ]); + } + + /** + * Resolve a multiple connection instance. + * + * @param array $names + * + * @return \Collective\Remote\MultiConnection + */ + public function multiple(array $names) + { + return new MultiConnection(array_map([$this, 'resolve'], $names)); + } + + /** + * Resolve a remote connection instance. + * + * @param string $name + * + * @return \Collective\Remote\Connection + */ + public function resolve($name) + { + return $this->makeConnection($name, $this->getConfig($name)); + } + + /** + * Make a new connection instance. + * + * @param string $name + * @param array $config + * + * @return \Collective\Remote\Connection + */ + protected function makeConnection($name, array $config) + { + $this->setOutput($connection = new Connection( + + $name, $config['host'], $config['username'], $this->getAuth($config) + + )); + + return $connection; + } + + /** + * Set the output implementation on the connection. + * + * @param \Collective\Remote\Connection $connection + * + * @return void + */ + protected function setOutput(Connection $connection) + { + $output = php_sapi_name() == 'cli' ? new ConsoleOutput() : new NullOutput(); + + $connection->setOutput($output); + } + + /** + * Format the appropriate authentication array payload. + * + * @param array $config + * + * @throws \InvalidArgumentException + * + * @return array + */ + protected function getAuth(array $config) + { + if (isset($config['agent']) && $config['agent'] === true) { + return ['agent' => true]; + } elseif (isset($config['key']) && trim($config['key']) != '') { + return ['key' => $config['key'], 'keyphrase' => $config['keyphrase']]; + } elseif (isset($config['keytext']) && trim($config['keytext']) != '') { + return ['keytext' => $config['keytext']]; + } elseif (isset($config['password'])) { + return ['password' => $config['password']]; + } + + throw new \InvalidArgumentException('Password / key is required.'); + } + + /** + * Get the configuration for a remote server. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return array + */ + protected function getConfig($name) + { + $config = $this->app['config'][ 'remote.connections.'.$name ]; + + if (!is_null($config)) { + return $config; + } + + throw new \InvalidArgumentException("Remote connection [$name] not defined."); + } + + /** + * Get the default connection name. + * + * @return string + */ + public function getDefaultConnection() + { + return $this->app['config']['remote.default']; + } + + /** + * Set the default connection name. + * + * @param string $name + * + * @return void + */ + public function setDefaultConnection($name) + { + $this->app['config']['remote.default'] = $name; + } + + /** + * Dynamically pass methods to the default connection. + * + * @param string $method + * @param array $parameters + * + * @return mixed + */ + public function __call($method, $parameters) + { + return call_user_func_array([$this->connection(), $method], $parameters); + } } diff --git a/src/RemoteServiceProvider.php b/src/RemoteServiceProvider.php index 4f7d2dd..acefaf5 100755 --- a/src/RemoteServiceProvider.php +++ b/src/RemoteServiceProvider.php @@ -1,78 +1,80 @@ - 'command.tail' - ]; - - /** - * Indicates if loading of the provider is deferred. - * - * @var bool - */ - protected $defer = true; - - public function boot() - { - $this->publishes([ - __DIR__.'/../config/remote.php' => config_path('remote.php'), - ]); - - $this->registerCommands(); - } - /** - * Register the service provider. - * - * @return void - */ - public function register() { - $this->app->bindShared( 'remote', function ( $app ) { - return new RemoteManager( $app ); - } ); - } - - /** - * Register the commands. - * - * @return void - */ - protected function registerCommands() - { - foreach (array_keys($this->commands) as $command) - { - $method = "register{$command}Command"; - call_user_func_array([$this, $method], []); - } - $this->commands(array_values($this->commands)); - } - - /** - * Register the command. - * - * @return void - */ - protected function registerTailCommand() - { - $this->app->singleton('command.tail', function ($app) - { - return new TailCommand(); - }); - } - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() { - return [ 'remote' ]; - } -} + 'command.tail', + ]; + + /** + * Indicates if loading of the provider is deferred. + * + * @var bool + */ + protected $defer = true; + + public function boot() + { + $this->publishes([ + __DIR__.'/../config/remote.php' => config_path('remote.php'), + ]); + + $this->registerCommands(); + } + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->app->bindShared('remote', function ($app) { + return new RemoteManager($app); + }); + } + + /** + * Register the commands. + * + * @return void + */ + protected function registerCommands() + { + foreach (array_keys($this->commands) as $command) { + $method = "register{$command}Command"; + call_user_func_array([$this, $method], []); + } + $this->commands(array_values($this->commands)); + } + + /** + * Register the command. + * + * @return void + */ + protected function registerTailCommand() + { + $this->app->singleton('command.tail', function ($app) { + return new TailCommand(); + }); + } + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return ['remote']; + } +} diff --git a/src/SecLibGateway.php b/src/SecLibGateway.php index 10e170f..d5e4c6c 100755 --- a/src/SecLibGateway.php +++ b/src/SecLibGateway.php @@ -1,310 +1,335 @@ -auth = $auth; - $this->files = $files; - $this->setHostAndPort( $host ); - } - - /** - * Set the host and port from a full host string. - * - * @param string $host - * - * @return void - */ - protected function setHostAndPort( $host ) { - if ( ! str_contains( $host, ':' ) ) { - $this->host = $host; - } else { - list( $this->host, $this->port ) = explode( ':', $host ); - - $this->port = (int) $this->port; - } - } - - /** - * Connect to the SSH server. - * - * @param string $username - * - * @return bool - */ - public function connect( $username ) { - return $this->getConnection()->login( $username, $this->getAuthForLogin() ); - } - - /** - * Determine if the gateway is connected. - * - * @return bool - */ - public function connected() { - return $this->getConnection()->isConnected(); - } - - /** - * Run a command against the server (non-blocking). - * - * @param string $command - * - * @return void - */ - public function run( $command ) { - $this->getConnection()->exec( $command, false ); - } - - /** - * Download the contents of a remote file. - * - * @param string $remote - * @param string $local - * - * @return void - */ - public function get( $remote, $local ) { - $this->getConnection()->get( $remote, $local ); - } - - /** - * Get the contents of a remote file. - * - * @param string $remote - * - * @return string - */ - public function getString( $remote ) { - return $this->getConnection()->get( $remote ); - } - - /** - * Upload a local file to the server. - * - * @param string $local - * @param string $remote - * - * @return void - */ - public function put( $local, $remote ) { - $this->getConnection()->put( $remote, $local, NET_SFTP_LOCAL_FILE ); - } - - /** - * Upload a string to to the given file on the server. - * - * @param string $remote - * @param string $contents - * - * @return void - */ - public function putString( $remote, $contents ) { - $this->getConnection()->put( $remote, $contents ); - } - - /** - * Get the next line of output from the server. - * - * @return string|null - */ - public function nextLine() { - $value = $this->getConnection()->_get_channel_packet( NET_SSH2_CHANNEL_EXEC ); - - return $value === true ? null : $value; - } - - /** - * Get the authentication object for login. - * - * @return \Crypt_RSA|\System_SSH_Agent|string - * @throws \InvalidArgumentException - */ - protected function getAuthForLogin() { - if ( $this->useAgent() ) { - return $this->getAgent(); - } - - // If a "key" was specified in the auth credentials, we will load it into a - // secure RSA key instance, which will be used to connect to the servers - // in place of a password, and avoids the developer specifying a pass. - elseif ( $this->hasRsaKey() ) { - return $this->loadRsaKey( $this->auth ); - } - - // If a plain password was set on the auth credentials, we will just return - // that as it can be used to connect to the server. This will be used if - // there is no RSA key and it gets specified in the credential arrays. - elseif ( isset( $this->auth['password'] ) ) { - return $this->auth['password']; - } - - throw new \InvalidArgumentException( 'Password / key is required.' ); - } - - /** - * Determine if an RSA key is configured. - * - * @return bool - */ - protected function hasRsaKey() { - $hasKey = ( isset( $this->auth['key'] ) && trim( $this->auth['key'] ) != '' ); - - return $hasKey || ( isset( $this->auth['keytext'] ) && trim( $this->auth['keytext'] ) != '' ); - } - - /** - * Load the RSA key instance. - * - * @param array $auth - * - * @return \Crypt_RSA - */ - protected function loadRsaKey( array $auth ) { - with( $key = $this->getKey( $auth ) )->loadKey( $this->readRsaKey( $auth ) ); - - return $key; - } - - /** - * Read the contents of the RSA key. - * - * @param array $auth - * - * @return string - */ - protected function readRsaKey( array $auth ) { - if ( isset( $auth['key'] ) ) { - return $this->files->get( $auth['key'] ); - } - - return $auth['keytext']; - } - - /** - * Create a new RSA key instance. - * - * @param array $auth - * - * @return \Crypt_RSA - */ - protected function getKey( array $auth ) { - with( $key = $this->getNewKey() )->setPassword( array_get( $auth, 'keyphrase' ) ); - - return $key; - } - - /** - * Determine if the SSH Agent should provide an RSA key. - * - * @return bool - */ - protected function useAgent() { - return isset( $this->auth['agent'] ) && $this->auth['agent'] === true; - } - - /** - * Get a new SSH Agent instance. - * - * @return \System_SSH_Agent - */ - public function getAgent() { - return new System_SSH_Agent; - } - - /** - * Get a new RSA key instance. - * - * @return \Crypt_RSA - */ - public function getNewKey() { - return new Crypt_RSA; - } - - /** - * Get the exit status of the last command. - * - * @return int|bool - */ - public function status() { - return $this->getConnection()->getExitStatus(); - } - - /** - * Get the host used by the gateway. - * - * @return string - */ - public function getHost() { - return $this->host; - } - - /** - * Get the port used by the gateway. - * - * @return int - */ - public function getPort() { - return $this->port; - } - - /** - * Get the underlying Net_SFTP connection. - * - * @return \Net_SFTP - */ - public function getConnection() { - if ( $this->connection ) { - return $this->connection; - } - - return $this->connection = new Net_SFTP( $this->host, $this->port ); - } +class SecLibGateway implements GatewayInterface +{ + /** + * The host name of the server. + * + * @var string + */ + protected $host; + + /** + * The SSH port on the server. + * + * @var int + */ + protected $port = 22; + + /** + * The authentication credential set. + * + * @var array + */ + protected $auth; + + /** + * The filesystem instance. + * + * @var \Illuminate\Filesystem\Filesystem + */ + protected $files; + + /** + * The SecLib connection instance. + * + * @var \Net_SFTP + */ + protected $connection; + + /** + * Create a new gateway implementation. + * + * @param string $host + * @param array $auth + * @param \Illuminate\Filesystem\Filesystem $files + */ + public function __construct($host, array $auth, Filesystem $files) + { + $this->auth = $auth; + $this->files = $files; + $this->setHostAndPort($host); + } + + /** + * Set the host and port from a full host string. + * + * @param string $host + * + * @return void + */ + protected function setHostAndPort($host) + { + if (!str_contains($host, ':')) { + $this->host = $host; + } else { + list($this->host, $this->port) = explode(':', $host); + + $this->port = (int) $this->port; + } + } + + /** + * Connect to the SSH server. + * + * @param string $username + * + * @return bool + */ + public function connect($username) + { + return $this->getConnection()->login($username, $this->getAuthForLogin()); + } + + /** + * Determine if the gateway is connected. + * + * @return bool + */ + public function connected() + { + return $this->getConnection()->isConnected(); + } + + /** + * Run a command against the server (non-blocking). + * + * @param string $command + * + * @return void + */ + public function run($command) + { + $this->getConnection()->exec($command, false); + } + + /** + * Download the contents of a remote file. + * + * @param string $remote + * @param string $local + * + * @return void + */ + public function get($remote, $local) + { + $this->getConnection()->get($remote, $local); + } + + /** + * Get the contents of a remote file. + * + * @param string $remote + * + * @return string + */ + public function getString($remote) + { + return $this->getConnection()->get($remote); + } + + /** + * Upload a local file to the server. + * + * @param string $local + * @param string $remote + * + * @return void + */ + public function put($local, $remote) + { + $this->getConnection()->put($remote, $local, NET_SFTP_LOCAL_FILE); + } + + /** + * Upload a string to to the given file on the server. + * + * @param string $remote + * @param string $contents + * + * @return void + */ + public function putString($remote, $contents) + { + $this->getConnection()->put($remote, $contents); + } + + /** + * Get the next line of output from the server. + * + * @return string|null + */ + public function nextLine() + { + $value = $this->getConnection()->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); + + return $value === true ? null : $value; + } + + /** + * Get the authentication object for login. + * + * @throws \InvalidArgumentException + * + * @return \Crypt_RSA|\System_SSH_Agent|string + */ + protected function getAuthForLogin() + { + if ($this->useAgent()) { + return $this->getAgent(); + } + + // If a "key" was specified in the auth credentials, we will load it into a + // secure RSA key instance, which will be used to connect to the servers + // in place of a password, and avoids the developer specifying a pass. + elseif ($this->hasRsaKey()) { + return $this->loadRsaKey($this->auth); + } + + // If a plain password was set on the auth credentials, we will just return + // that as it can be used to connect to the server. This will be used if + // there is no RSA key and it gets specified in the credential arrays. + elseif (isset($this->auth['password'])) { + return $this->auth['password']; + } + + throw new \InvalidArgumentException('Password / key is required.'); + } + + /** + * Determine if an RSA key is configured. + * + * @return bool + */ + protected function hasRsaKey() + { + $hasKey = (isset($this->auth['key']) && trim($this->auth['key']) != ''); + + return $hasKey || (isset($this->auth['keytext']) && trim($this->auth['keytext']) != ''); + } + + /** + * Load the RSA key instance. + * + * @param array $auth + * + * @return \Crypt_RSA + */ + protected function loadRsaKey(array $auth) + { + with($key = $this->getKey($auth))->loadKey($this->readRsaKey($auth)); + + return $key; + } + + /** + * Read the contents of the RSA key. + * + * @param array $auth + * + * @return string + */ + protected function readRsaKey(array $auth) + { + if (isset($auth['key'])) { + return $this->files->get($auth['key']); + } + + return $auth['keytext']; + } + + /** + * Create a new RSA key instance. + * + * @param array $auth + * + * @return \Crypt_RSA + */ + protected function getKey(array $auth) + { + with($key = $this->getNewKey())->setPassword(array_get($auth, 'keyphrase')); + + return $key; + } + + /** + * Determine if the SSH Agent should provide an RSA key. + * + * @return bool + */ + protected function useAgent() + { + return isset($this->auth['agent']) && $this->auth['agent'] === true; + } + + /** + * Get a new SSH Agent instance. + * + * @return \System_SSH_Agent + */ + public function getAgent() + { + return new System_SSH_Agent(); + } + + /** + * Get a new RSA key instance. + * + * @return \Crypt_RSA + */ + public function getNewKey() + { + return new Crypt_RSA(); + } + + /** + * Get the exit status of the last command. + * + * @return int|bool + */ + public function status() + { + return $this->getConnection()->getExitStatus(); + } + + /** + * Get the host used by the gateway. + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Get the port used by the gateway. + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Get the underlying Net_SFTP connection. + * + * @return \Net_SFTP + */ + public function getConnection() + { + if ($this->connection) { + return $this->connection; + } + + return $this->connection = new Net_SFTP($this->host, $this->port); + } } diff --git a/tests/RemoteSecLibTest.php b/tests/RemoteSecLibTest.php index 2a5c109..cc5a2ea 100644 --- a/tests/RemoteSecLibTest.php +++ b/tests/RemoteSecLibTest.php @@ -1,52 +1,57 @@ getGateway(); + $this->assertEquals('127.0.0.1', $gateway->getHost()); + $this->assertEquals(22, $gateway->getPort()); + } - public function testHostAndPortSetCorrectly() { - $gateway = $this->getGateway(); - $this->assertEquals( '127.0.0.1', $gateway->getHost() ); - $this->assertEquals( 22, $gateway->getPort() ); - } + public function testConnectProperlyCallsLoginWithAuth() + { + $gateway = $this->getGateway(); + $gateway->shouldReceive('getNewKey')->andReturn($key = m::mock('StdClass')); + $key->shouldReceive('setPassword')->once()->with('keyphrase'); + $key->shouldReceive('loadKey')->once()->with('keystuff'); + $gateway->getConnection()->shouldReceive('login')->with('taylor', $key); + $gateway->connect('taylor'); + } - public function testConnectProperlyCallsLoginWithAuth() { - $gateway = $this->getGateway(); - $gateway->shouldReceive( 'getNewKey' )->andReturn( $key = m::mock( 'StdClass' ) ); - $key->shouldReceive( 'setPassword' )->once()->with( 'keyphrase' ); - $key->shouldReceive( 'loadKey' )->once()->with( 'keystuff' ); - $gateway->getConnection()->shouldReceive( 'login' )->with( 'taylor', $key ); - $gateway->connect( 'taylor' ); - } + public function testKeyTextCanBeSetManually() + { + $files = m::mock('Illuminate\Filesystem\Filesystem'); + $gateway = m::mock('Collective\Remote\SecLibGateway', [ + '127.0.0.1:22', + ['username' => 'taylor', 'keytext' => 'keystuff'], + $files, + ])->makePartial(); + $gateway->shouldReceive('getConnection')->andReturn(m::mock('StdClass')); + $gateway->shouldReceive('getNewKey')->andReturn($key = m::mock('StdClass')); + $key->shouldReceive('setPassword')->once()->with(null); + $key->shouldReceive('loadKey')->once()->with('keystuff'); + $gateway->getConnection()->shouldReceive('login')->with('taylor', $key); + $gateway->connect('taylor'); + } - public function testKeyTextCanBeSetManually() { - $files = m::mock( 'Illuminate\Filesystem\Filesystem' ); - $gateway = m::mock( 'Collective\Remote\SecLibGateway', [ - '127.0.0.1:22', - [ 'username' => 'taylor', 'keytext' => 'keystuff' ], - $files - ] )->makePartial(); - $gateway->shouldReceive( 'getConnection' )->andReturn( m::mock( 'StdClass' ) ); - $gateway->shouldReceive( 'getNewKey' )->andReturn( $key = m::mock( 'StdClass' ) ); - $key->shouldReceive( 'setPassword' )->once()->with( null ); - $key->shouldReceive( 'loadKey' )->once()->with( 'keystuff' ); - $gateway->getConnection()->shouldReceive( 'login' )->with( 'taylor', $key ); - $gateway->connect( 'taylor' ); - } + public function getGateway() + { + $files = m::mock('Illuminate\Filesystem\Filesystem'); + $files->shouldReceive('get')->with('keypath')->andReturn('keystuff'); + $gateway = m::mock('Collective\Remote\SecLibGateway', [ + '127.0.0.1:22', + ['username' => 'taylor', 'key' => 'keypath', 'keyphrase' => 'keyphrase'], + $files, + ])->makePartial(); + $gateway->shouldReceive('getConnection')->andReturn(m::mock('StdClass')); - public function getGateway() { - $files = m::mock( 'Illuminate\Filesystem\Filesystem' ); - $files->shouldReceive( 'get' )->with( 'keypath' )->andReturn( 'keystuff' ); - $gateway = m::mock( 'Collective\Remote\SecLibGateway', [ - '127.0.0.1:22', - [ 'username' => 'taylor', 'key' => 'keypath', 'keyphrase' => 'keyphrase' ], - $files - ] )->makePartial(); - $gateway->shouldReceive( 'getConnection' )->andReturn( m::mock( 'StdClass' ) ); - - return $gateway; - } - } \ No newline at end of file + return $gateway; + } +}