protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $app = $this->selectApp($input); $environment = $this->getSelectedEnvironment(); $cacheKey = implode('-', ['relationships', $environment->id . $environment->project . $app]); $cache = CacheUtil::getCache(); $relationships = $cache->fetch($cacheKey); if (empty($relationships) || $input->getOption('refresh')) { $util = new RelationshipsUtil($this->stdErr); $sshUrl = $environment->getSshUrl($app); $relationships = $util->getRelationships($sshUrl); if (empty($relationships)) { $this->stdErr->writeln('No relationships found'); return 1; } $cache->save($cacheKey, $relationships, 3600); } $value = $relationships; $key = null; if ($property = $input->getOption('property')) { $parents = explode('.', $property); $key = end($parents); $value = Util::getNestedArrayValue($relationships, $parents, $key_exists); if (!$key_exists) { $this->stdErr->writeln("Relationship property not found: <error>{$property}</error>"); return 1; } } $formatter = new PropertyFormatter(); $formatter->yamlInline = 10; $output->writeln($formatter->format($value, $key)); return 0; }
protected function execute(InputInterface $input, OutputInterface $output) { if (!$this->validateInput($input, $output)) { return 1; } $sshUrl = $this->getSelectedEnvironment()->getSshUrl($input->getOption('app')); $util = new RelationshipsUtil($output); $relationships = $util->getRelationships($sshUrl); if (!$relationships) { $output->writeln('No relationships found'); return 1; } foreach ($relationships as $key => $relationship) { foreach ($relationship as $delta => $info) { $output->writeln("<comment>{$key}:{$delta}:</comment>"); foreach ($info as $prop => $value) { if (is_scalar($value)) { $propString = str_pad("{$prop}", 10, " "); $output->writeln("<info> {$propString}: {$value}</info>"); } } } } return 0; }
protected function execute(InputInterface $input, OutputInterface $output) { if (!$this->validateInput($input, $output)) { return 1; } $sshOptions = ''; $sshUrl = $this->getSelectedEnvironment()->getSshUrl($input->getOption('app')); $util = new RelationshipsUtil($output); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } $sqlCommand = "mysql --no-auto-rehash --database={$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; $query = $input->getArgument('query'); if ($query) { $sqlCommand .= ' --execute ' . escapeshellarg($query) . ' 2>&1'; } else { $sshOptions .= ' -qt'; } $command = 'ssh' . $sshOptions . ' ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($sqlCommand); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $output->writeln("Running command: <info>{$command}</info>"); } passthru($command, $return_var); return $return_var; }
protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $environment = $this->getSelectedEnvironment(); $appName = $this->selectApp($input); $sshUrl = $environment->getSshUrl($appName); if (!$input->getOption('stdout')) { if ($input->getOption('file')) { $dumpFile = $input->getOption('file'); /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */ $fsHelper = $this->getHelper('fs'); $dumpFile = $fsHelper->makePathAbsolute($dumpFile); if (is_dir($dumpFile)) { $dumpFile .= "/{$environment->id}-dump.sql"; } } else { if (!($projectRoot = $this->getProjectRoot())) { throw new RootNotFoundException('Project root not found. Specify --file or go to a project directory.'); } $dumpFile = $projectRoot . '/' . $environment->id; if ($appName) { $dumpFile .= '--' . $appName; } $dumpFile .= '-dump.sql'; } } if (isset($dumpFile)) { if (file_exists($dumpFile)) { /** @var \Platformsh\Cli\Helper\PlatformQuestionHelper $questionHelper */ $questionHelper = $this->getHelper('question'); if (!$questionHelper->confirm("File exists: <comment>{$dumpFile}</comment>. Overwrite?", $input, $this->stdErr, false)) { return 1; } } $this->stdErr->writeln("Creating SQL dump file: <info>{$dumpFile}</info>"); } $util = new RelationshipsUtil($this->stdErr); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } switch ($database['scheme']) { case 'pgsql': $dumpCommand = "pg_dump --clean" . " postgresql://{$database['username']}:{$database['password']}@{$database['host']}/{$database['path']}"; break; default: $dumpCommand = "mysqldump --no-autocommit --single-transaction" . " --opt -Q {$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; break; } set_time_limit(0); $command = 'ssh ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($dumpCommand); if (isset($dumpFile)) { $command .= ' > ' . escapeshellarg($dumpFile); } $this->debug("Running command: <info>{$command}</info>"); passthru($command, $return_var); return $return_var; }
protected function execute(InputInterface $input, OutputInterface $output) { if (!$this->validateInput($input, $output)) { return 1; } $projectRoot = $this->getProjectRoot(); $dumpFile = $input->getOption('file'); if ($dumpFile !== null) { if (file_exists($dumpFile)) { $output->writeln("File exists: <error>{$dumpFile}</error>"); return 1; } $dir = dirname($dumpFile); if (!is_dir($dir)) { $output->writeln("Directory not found: <error>{$dir}</error>"); return 1; } elseif (!is_writable($dir)) { $output->writeln("Directory not writable: <error>{$dir}</error>"); return 1; } $dumpFile = realpath($dir) . '/' . basename($dumpFile); } else { $dumpFile = $projectRoot . '/dump.sql'; if (file_exists($dumpFile)) { $output->writeln("File exists: <error>{$dumpFile}</error>"); return 1; } } $output->writeln("Creating SQL dump file: <info>{$dumpFile}</info>"); $sshUrl = $this->getSelectedEnvironment()->getSshUrl($input->getOption('app')); $util = new RelationshipsUtil($output); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } // @todo correct dump command for pgsql and other DBs $dumpCommand = "mysqldump --no-autocommit --single-transaction" . " --opt -Q {$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; set_time_limit(0); $command = 'ssh ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($dumpCommand) . ' > ' . escapeshellarg($dumpFile); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $output->writeln("Running command: <info>{$command}</info>"); } passthru($command, $return_var); return $return_var; }
protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); if (!$input->getArgument('query') && $this->runningViaMulti) { throw new \InvalidArgumentException('The query argument is required when running via "multi"'); } $sshOptions = ''; $sshUrl = $this->getSelectedEnvironment()->getSshUrl($this->selectApp($input)); $util = new RelationshipsUtil($this->stdErr); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } switch ($database['scheme']) { case 'pgsql': $sqlCommand = "psql postgresql://{$database['username']}:{$database['password']}@{$database['host']}/{$database['path']}"; $queryOption = ' -c '; break; default: $sqlCommand = "mysql --no-auto-rehash --database={$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; $queryOption = ' --execute '; break; } $query = $input->getArgument('query'); if ($query) { $sqlCommand .= $queryOption . escapeshellarg($query) . ' 2>&1'; } // Switch on pseudo-tty allocation when there is a local tty. if ($this->isTerminal($output)) { $sshOptions .= ' -t'; } if ($output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { $sshOptions .= ' -vv'; } elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) { $sshOptions .= ' -v'; } elseif ($output->getVerbosity() <= OutputInterface::VERBOSITY_VERBOSE) { $sshOptions .= ' -q'; } $command = 'ssh' . $sshOptions . ' ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($sqlCommand); return $this->getHelper('shell')->executeSimple($command); }
protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $sshOptions = ''; $sshUrl = $this->getSelectedEnvironment()->getSshUrl($input->getOption('app')); $util = new RelationshipsUtil($this->stdErr); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } switch ($database['scheme']) { case 'pgsql': $sqlCommand = "psql postgresql://{$database['username']}:{$database['password']}@{$database['host']}/{$database['path']}"; break; default: $sqlCommand = "mysql --no-auto-rehash --database={$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; break; } $query = $input->getArgument('query'); if ($query) { $sqlCommand .= ' --execute ' . escapeshellarg($query) . ' 2>&1'; } // Switch on pseudo-tty allocation when there is a local tty. if ($this->isTerminal($output)) { $sshOptions .= ' -t'; } if ($output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { $sshOptions .= ' -vv'; } elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) { $sshOptions .= ' -v'; } elseif ($output->getVerbosity() <= OutputInterface::VERBOSITY_VERBOSE) { $sshOptions .= ' -q'; } $command = 'ssh' . $sshOptions . ' ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($sqlCommand); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $this->stdErr->writeln("Running command: <info>{$command}</info>"); } passthru($command, $return_var); return $return_var; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $project = $this->getSelectedProject(); $environment = $this->getSelectedEnvironment(); if ($environment->id === 'master') { $this->stdErr->writeln('Tunnelling to the <error>master</error> environment is not safe.'); if (count($this->getEnvironments($project)) === 1) { $this->stdErr->writeln('Use <info>platform branch</info> to create a new development environment.'); } return 1; } $appName = $this->selectApp($input); $sshUrl = $environment->getSshUrl($appName); $util = new RelationshipsUtil($this->stdErr); $relationships = $util->getRelationships($sshUrl); if (!$relationships) { $this->stdErr->writeln('No relationships found.'); return 1; } $logFile = $this->getConfigDir() . '/tunnels.log'; if (!($log = $this->openLog($logFile))) { $this->stdErr->writeln(sprintf('Failed to open log file for writing: %s', $logFile)); return 1; } $extraSshArgs = []; if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) { $extraSshArgs[] = '-vv'; } elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $extraSshArgs[] = '-v'; } $log->setVerbosity($output->getVerbosity()); $processManager = new ProcessManager(); $processManager->fork(); $error = false; $processIds = []; foreach ($relationships as $relationship => $services) { foreach ($services as $serviceKey => $service) { $remoteHost = $service['host']; $remotePort = $service['port']; $localPort = $this->getPort(); $tunnel = ['projectId' => $project->id, 'environmentId' => $environment->id, 'appName' => $appName, 'relationship' => $relationship, 'serviceKey' => $serviceKey, 'remotePort' => $remotePort, 'remoteHost' => $remoteHost, 'localPort' => $localPort, 'service' => $service, 'pid' => null]; $relationshipString = $this->formatTunnelRelationship($tunnel); if ($openTunnelInfo = $this->isTunnelOpen($tunnel)) { $this->stdErr->writeln("A tunnel is already open for the relationship: <info>{$relationshipString}</info>"); continue; } $process = $this->createTunnelProcess($sshUrl, $remoteHost, $remotePort, $localPort, $extraSshArgs); $pidFile = $this->getPidFile($tunnel); try { $pid = $processManager->startProcess($process, $pidFile, $log); } catch (\Exception $e) { $this->stdErr->writeln(sprintf('Failed to open tunnel for relationship <error>%s</error>: %s', $relationshipString, $e->getMessage())); $error = true; continue; } // Wait a very small time to capture any immediate errors. usleep(100000); if (!$process->isRunning() && !$process->isSuccessful()) { $this->stdErr->writeln(trim($process->getErrorOutput())); $this->stdErr->writeln(sprintf('Failed to open tunnel for relationship: <error>%s</error>', $relationshipString)); unlink($pidFile); $error = true; continue; } // Save information about the tunnel for use in other commands. $tunnel['pid'] = $pid; $this->tunnelInfo[] = $tunnel; $this->saveTunnelInfo(); $this->stdErr->writeln(sprintf('SSH tunnel opened on port %s to relationship: <info>%s</info>', $localPort, $relationshipString)); $processIds[] = $pid; } } if (count($processIds)) { $this->stdErr->writeln("Logs are written to: {$logFile}"); } if (!$error) { $this->stdErr->writeln(''); $this->stdErr->writeln("List tunnels with: <info>platform tunnels</info>"); $this->stdErr->writeln("View tunnel details with: <info>platform tunnel:info</info>"); $this->stdErr->writeln("Close tunnels with: <info>platform tunnel:close</info>"); } $processManager->killParent($error); $processManager->monitor($log); return 0; }
protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $projectRoot = $this->getProjectRoot(); if (!$projectRoot) { throw new RootNotFoundException(); } $appName = $this->selectApp($input); $sshUrl = $this->getSelectedEnvironment()->getSshUrl($appName); // Get and parse app config. $app = LocalApplication::getApplication($appName, $projectRoot); $appConfig = $app->getConfig(); if (empty($appConfig['relationships'])) { $this->stdErr->writeln('No application relationships found.'); return 1; } $util = new RelationshipsUtil($this->stdErr); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { $this->stdErr->writeln('No database selected.'); return 1; } // Find the database's service name in the relationships. $dbServiceName = false; foreach ($appConfig['relationships'] as $relationshipName => $relationship) { if ($database['_relationship_name'] === $relationshipName) { list($dbServiceName, ) = explode(':', $relationship, 2); break; } } if (!$dbServiceName) { $this->stdErr->writeln('Service name not found for relationship: ' . $database['_relationship_name']); return 1; } // Load services yaml. $services = Yaml::parse(file_get_contents($projectRoot . '/.platform/services.yaml')); if (!empty($services[$dbServiceName]['disk'])) { $allocatedDisk = $services[$dbServiceName]['disk']; } else { $this->stdErr->writeln('The allocated disk size could not be determined for service: ' . $dbServiceName); return 1; } $this->stdErr->write('Querying database <comment>' . $dbServiceName . '</comment> to estimate disk usage. '); $this->stdErr->writeln('This might take a while.'); /** @var ShellHelper $shellHelper */ $shellHelper = $this->getHelper('shell'); $command = ['ssh']; $command[] = $sshUrl; switch ($database['scheme']) { case 'pgsql': $command[] = $this->psqlQuery($database); $result = $shellHelper->execute($command); $resultArr = explode(PHP_EOL, $result); $estimatedUsage = array_sum($resultArr) / 1048576; break; default: $command[] = $this->mysqlQuery($database); $estimatedUsage = $shellHelper->execute($command); break; } $percentsUsed = $estimatedUsage * 100 / $allocatedDisk; $table = new Table($input, $output); $propertyNames = ['max', 'used', 'percent_used']; $machineReadable = $table->formatIsMachineReadable(); $values = [(int) $allocatedDisk . ($machineReadable ? '' : 'MB'), (int) $estimatedUsage . ($machineReadable ? '' : 'MB'), (int) $percentsUsed . '%']; $table->renderSimple($values, $propertyNames); return 0; }
protected function execute(InputInterface $input, OutputInterface $output) { $this->validateInput($input); $project = $this->getSelectedProject(); $environment = $this->getSelectedEnvironment(); $appName = $this->selectApp($input); $sshUrl = $environment->getSshUrl($appName); $timestamp = $input->getOption('timestamp') ? str_replace('+', '', date('Ymd-His-O')) : null; if (!$input->getOption('stdout')) { if ($input->getOption('file')) { $dumpFile = rtrim($input->getOption('file'), '/'); /** @var \Platformsh\Cli\Helper\FilesystemHelper $fsHelper */ $fsHelper = $this->getHelper('fs'); // Insert the timestamp into the filename. if ($timestamp) { $basename = basename($dumpFile); $prefix = substr($dumpFile, 0, -strlen($basename)); if ($dotPos = strrpos($basename, '.')) { $basename = substr($basename, 0, $dotPos) . '--' . $timestamp . substr($basename, $dotPos); } else { $basename .= '--' . $timestamp; } $dumpFile = $prefix . $basename; } // Make the filename absolute. $dumpFile = $fsHelper->makePathAbsolute($dumpFile); // Ensure the filename is not a directory. if (is_dir($dumpFile)) { $dumpFile .= '/' . $this->getDefaultDumpFilename($project, $environment, $appName, $timestamp); } } else { $projectRoot = $this->getProjectRoot(); $directory = $projectRoot ?: getcwd(); $dumpFile = $directory . '/' . $this->getDefaultDumpFilename($project, $environment, $appName, $timestamp); } } if (isset($dumpFile)) { if (file_exists($dumpFile)) { /** @var \Platformsh\Cli\Helper\QuestionHelper $questionHelper */ $questionHelper = $this->getHelper('question'); if (!$questionHelper->confirm("File exists: <comment>{$dumpFile}</comment>. Overwrite?", false)) { return 1; } } $this->stdErr->writeln("Creating SQL dump file: <info>{$dumpFile}</info>"); } $util = new RelationshipsUtil($this->stdErr); $database = $util->chooseDatabase($sshUrl, $input); if (empty($database)) { return 1; } switch ($database['scheme']) { case 'pgsql': $dumpCommand = "pg_dump --clean" . " postgresql://{$database['username']}:{$database['password']}@{$database['host']}/{$database['path']}"; break; default: $dumpCommand = "mysqldump --no-autocommit --single-transaction" . " --opt -Q {$database['path']}" . " --host={$database['host']} --port={$database['port']}" . " --user={$database['username']} --password={$database['password']}"; break; } set_time_limit(0); $command = 'ssh -C ' . escapeshellarg($sshUrl) . ' ' . escapeshellarg($dumpCommand); if (isset($dumpFile)) { $command .= ' > ' . escapeshellarg($dumpFile); } return $this->getHelper('shell')->executeSimple($command); }