Skip to content

Commit 367172e

Browse files
committed
feat: add support for private database to database:export command
1 parent efc5d37 commit 367172e

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

src/Command/Cache/CacheTunnelCommand.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Ymir\Cli\Command\Network\AddBastionHostCommand;
2424
use Ymir\Cli\Console\OutputInterface;
2525
use Ymir\Cli\ProjectConfiguration\ProjectConfiguration;
26+
use Ymir\Cli\Support\Arr;
2627
use Ymir\Cli\Tool\Ssh;
2728

2829
class CacheTunnelCommand extends AbstractCacheCommand
@@ -82,7 +83,7 @@ protected function perform(InputInterface $input, OutputInterface $output)
8283
throw new RuntimeException(sprintf('The "%s" cache isn\'t available', $cache['name']));
8384
}
8485

85-
$network = $this->apiClient->getNetwork($cache['network']['id']);
86+
$network = $this->apiClient->getNetwork(Arr::get($cache, 'network.id'));
8687

8788
if (!is_array($network->get('bastion_host'))) {
8889
throw new RuntimeException(sprintf('The cache network does\'t have a bastion host to connect to. You can add one to the network with the "%s" command.', AddBastionHostCommand::NAME));
@@ -96,10 +97,10 @@ protected function perform(InputInterface $input, OutputInterface $output)
9697

9798
$output->info(sprintf('Creating SSH tunnel to the "<comment>%s</comment>" cache cluster. You can connect using: <comment>localhost:%s</comment>', $cache['name'], $localPort));
9899

99-
$process = Ssh::tunnelBastionHost($network->get('bastion_host'), $localPort, $cache['endpoint'], 6379);
100+
$tunnel = Ssh::tunnelBastionHost($network->get('bastion_host'), $localPort, $cache['endpoint'], 6379);
100101

101102
$output->info('Once finished, press "<comment>Ctrl+C</comment>" to close the tunnel');
102103

103-
$process->wait();
104+
$tunnel->wait();
104105
}
105106
}

src/Command/Database/DatabaseServerTunnelCommand.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Ymir\Cli\Command\Network\AddBastionHostCommand;
2424
use Ymir\Cli\Console\OutputInterface;
2525
use Ymir\Cli\ProjectConfiguration\ProjectConfiguration;
26+
use Ymir\Cli\Support\Arr;
2627
use Ymir\Cli\Tool\Ssh;
2728

2829
class DatabaseServerTunnelCommand extends AbstractDatabaseCommand
@@ -84,7 +85,7 @@ protected function perform(InputInterface $input, OutputInterface $output)
8485
throw new RuntimeException(sprintf('The "%s" database server is publicly accessible and isn\'t on a private subnet', $databaseServer['name']));
8586
}
8687

87-
$network = $this->apiClient->getNetwork($databaseServer['network']['id']);
88+
$network = $this->apiClient->getNetwork(Arr::get($databaseServer, 'network.id'));
8889

8990
if (!is_array($network->get('bastion_host'))) {
9091
throw new RuntimeException(sprintf('The database server network does\'t have a bastion host to connect to. You can add one to the network with the "%s" command.', AddBastionHostCommand::NAME));
@@ -98,10 +99,10 @@ protected function perform(InputInterface $input, OutputInterface $output)
9899

99100
$output->info(sprintf('Creating SSH tunnel to the "<comment>%s</comment>" database server. You can connect using: <comment>localhost:%s</comment>', $databaseServer['name'], $localPort));
100101

101-
$process = Ssh::tunnelBastionHost($network->get('bastion_host'), $localPort, $databaseServer['endpoint'], 3306);
102+
$tunnel = Ssh::tunnelBastionHost($network->get('bastion_host'), $localPort, $databaseServer['endpoint'], 3306);
102103

103104
$output->info('Once finished, press "<comment>Ctrl+C</comment>" to close the tunnel');
104105

105-
$process->wait();
106+
$tunnel->wait();
106107
}
107108
}

src/Command/Database/ExportDatabaseCommand.php

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
use Symfony\Component\Filesystem\Filesystem;
2121
use Ymir\Cli\ApiClient;
2222
use Ymir\Cli\CliConfiguration;
23+
use Ymir\Cli\Command\Network\AddBastionHostCommand;
2324
use Ymir\Cli\Console\OutputInterface;
2425
use Ymir\Cli\Process\Process;
2526
use Ymir\Cli\ProjectConfiguration\ProjectConfiguration;
27+
use Ymir\Cli\Support\Arr;
28+
use Ymir\Cli\Tool\Ssh;
2629

2730
class ExportDatabaseCommand extends AbstractDatabaseCommand
2831
{
@@ -77,18 +80,10 @@ protected function configure()
7780
protected function perform(InputInterface $input, OutputInterface $output)
7881
{
7982
$databaseServer = $this->determineDatabaseServer('Which database server would you like to export a database from?', $input, $output);
80-
$name = $this->getStringArgument($input, 'name');
83+
$name = $this->determineDatabaseName($databaseServer, $input, $output);
8184
$user = $this->getStringArgument($input, 'user');
8285
$password = $this->getStringArgument($input, 'password');
8386

84-
$databases = $this->apiClient->getDatabases($databaseServer['id']);
85-
86-
if (empty($name)) {
87-
$name = $output->choice('Which database would you like to export?', $databases);
88-
} elseif (!$databases->has($name)) {
89-
throw new RuntimeException(sprintf('The "%s" database doesn\'t exist on the "%s" database server', $name, $databaseServer['name']));
90-
}
91-
9287
if (empty($user)) {
9388
$user = $output->ask('Which user do you want to use to connect to the database server?', 'ymir');
9489
}
@@ -103,10 +98,59 @@ protected function perform(InputInterface $input, OutputInterface $output)
10398
return;
10499
}
105100

101+
$host = $databaseServer['endpoint'];
102+
$port = 3306;
103+
$tunnel = null;
104+
105+
if (!$databaseServer['publicly_accessible']) {
106+
$output->info(sprintf('Opening SSH tunnel to "<comment>%s</comment>" database server', $databaseServer['name']));
107+
108+
$tunnel = $this->startSshTunnel($databaseServer);
109+
$host = '127.0.0.1';
110+
$port = '3305';
111+
112+
// Need to wait a bit while SSH connection opens
113+
sleep(1);
114+
}
115+
106116
$output->infoWithDelayWarning('Exporting database');
107117

108-
Process::runShellCommandline(sprintf('mysqldump --quick --single-transaction --default-character-set=utf8mb4 --host=%s --user=%s --password=%s %s | gzip > %s', $databaseServer['endpoint'], $user, $password, $name, $filename));
118+
Process::runShellCommandline(sprintf('mysqldump --quick --single-transaction --default-character-set=utf8mb4 --host=%s --port=%s --user=%s --password=%s %s | gzip > %s', $host, $port, $user, $password, $name, $filename));
119+
120+
if ($tunnel instanceof Process) {
121+
$tunnel->stop();
122+
}
109123

110124
$output->infoWithValue('Database exported successfully to', $filename);
111125
}
126+
127+
/**
128+
* Determine the name of the database to export.
129+
*/
130+
private function determineDatabaseName(array $databaseServer, InputInterface $input, OutputInterface $output): string
131+
{
132+
$name = $this->getStringArgument($input, 'name');
133+
134+
if (!empty($name)) {
135+
return $name;
136+
} elseif (empty($name) && !$databaseServer['publicly_accessible']) {
137+
throw new RuntimeException('You must specify the name of the database to export for a private database server');
138+
}
139+
140+
return $output->choice('Which database would you like to export?', $this->apiClient->getDatabases($databaseServer['id']));
141+
}
142+
143+
/**
144+
* Start a SSH tunnel to a private database server.
145+
*/
146+
private function startSshTunnel(array $databaseServer): Process
147+
{
148+
$network = $this->apiClient->getNetwork(Arr::get($databaseServer, 'network.id'));
149+
150+
if (!is_array($network->get('bastion_host'))) {
151+
throw new RuntimeException(sprintf('The database server network does\'t have a bastion host to connect to. You can add one to the network with the "%s" command.', AddBastionHostCommand::NAME));
152+
}
153+
154+
return Ssh::tunnelBastionHost($network->get('bastion_host'), 3305, $databaseServer['endpoint'], 3306);
155+
}
112156
}

0 commit comments

Comments
 (0)