Commit 99754f22 authored by Cédric Anne's avatar Cédric Anne Committed by Johan Cwiklinski

Fix and enhance install command (#6286)

* Fix installation command when configuration is already set

* Force definition of DB user in installation command

* Always display used DB configuration in install command
parent 5b705d51
......@@ -188,7 +188,6 @@ abstract class AbstractConfigureCommand extends AbstractCommand implements Force
$db_hostport = $db_host . (!empty($db_port) ? ':' . $db_port : '');
$reconfigure = $input->getOption('reconfigure');
$no_interaction = $input->getOption('no-interaction'); // Base symfony/console option
if (file_exists(GLPI_CONFIG_DIR . '/config_db.php') && !$reconfigure) {
// Prevent overriding of existing DB
......@@ -198,42 +197,21 @@ abstract class AbstractConfigureCommand extends AbstractCommand implements Force
return self::ERROR_DB_CONFIG_ALREADY_SET;
}
if (empty($db_name)) {
throw new InvalidArgumentException(
__('Database name defined by --db-name option cannot be empty.')
);
}
$this->validateConfigInput($input);
if (null === $db_pass) {
// Will be null if option used without value and without interaction
throw new InvalidArgumentException(
__('--db-password option value cannot be null.')
);
}
if (!$no_interaction) {
// Ask for confirmation (unless --no-interaction)
$informations = new Table($output);
$informations->addRow([__('Database host'), $db_hostport]);
$informations->addRow([__('Database name'), $db_name]);
$informations->addRow([__('Database user'), $db_user]);
$informations->render();
/** @var Symfony\Component\Console\Helper\QuestionHelper $question_helper */
$question_helper = $this->getHelper('question');
$run = $question_helper->ask(
$input,
$output,
new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
$run = $this->askForDbConfigConfirmation(
$input,
$output,
$db_hostport,
$db_name,
$db_user
);
if (!$run) {
$output->writeln(
'<comment>' . __('Configuration aborted.') . '</comment>',
OutputInterface::VERBOSITY_VERBOSE
);
if (!$run) {
$output->writeln(
'<comment>' . __('Configuration aborted.') . '</comment>',
OutputInterface::VERBOSITY_VERBOSE
);
return self::ABORTED_BY_USER;
}
return self::ABORTED_BY_USER;
}
$mysqli = new \mysqli();
......@@ -283,4 +261,82 @@ abstract class AbstractConfigureCommand extends AbstractCommand implements Force
return true;
}
/**
* Check if DB is already configured.
*
* @return boolean
*/
protected function isDbAlreadyConfigured() {
return file_exists(GLPI_CONFIG_DIR . '/config_db.php');
}
/**
* Validate configuration variables from input.
*
* @param InputInterface $input
*
* @throws InvalidArgumentException
*/
protected function validateConfigInput(InputInterface $input) {
$db_name = $input->getOption('db-name');
$db_user = $input->getOption('db-user');
$db_pass = $input->getOption('db-password');
if (empty($db_name)) {
throw new InvalidArgumentException(
__('Database name defined by --db-name option cannot be empty.')
);
}
if (empty($db_user)) {
throw new InvalidArgumentException(
__('Database user defined by --db-user option cannot be empty.')
);
}
if (null === $db_pass) {
// Will be null if option used without value and without interaction
throw new InvalidArgumentException(
__('--db-password option value cannot be null.')
);
}
}
/**
* Ask user to confirm DB configuration.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return boolean
*/
protected function askForDbConfigConfirmation(
InputInterface $input,
OutputInterface $output,
$db_hostport,
$db_name,
$db_user) {
$informations = new Table($output);
$informations->addRow([__('Database host'), $db_hostport]);
$informations->addRow([__('Database name'), $db_name]);
$informations->addRow([__('Database user'), $db_user]);
$informations->render();
if ($input->getOption('no-interaction')) {
// Consider that config is validated if user require no interaction
return true;
}
/** @var Symfony\Component\Console\Helper\QuestionHelper $question_helper */
$question_helper = $this->getHelper('question');
return $question_helper->ask(
$input,
$output,
new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
);
}
}
......@@ -39,10 +39,11 @@ if (!defined('GLPI_ROOT')) {
use DB;
use Toolbox;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class InstallCommand extends AbstractConfigureCommand {
......@@ -93,7 +94,25 @@ class InstallCommand extends AbstractConfigureCommand {
protected function interact(InputInterface $input, OutputInterface $output) {
if ($this->shouldSetDBConfig($input, $output)) {
if ($this->isDbAlreadyConfigured()
&& $this->isInputContainingConfigValues($input, $output)
&& !$input->getOption('reconfigure')) {
/** @var Symfony\Component\Console\Helper\QuestionHelper $question_helper */
$question_helper = $this->getHelper('question');
$reconfigure = $question_helper->ask(
$input,
$output,
new ConfirmationQuestion(
__('Command input contains configuration options that may override existing configuration.')
. PHP_EOL
. __('Do you want to reconfigure database ?') . ' [Yes/no]',
true
)
);
$input->setOption('reconfigure', $reconfigure);
}
if (!$this->isDbAlreadyConfigured() || $input->getOption('reconfigure')) {
parent::interact($input, $output);
}
}
......@@ -102,16 +121,21 @@ class InstallCommand extends AbstractConfigureCommand {
global $DB;
$db_pass = $input->getOption('db-password');
$db_host = $input->getOption('db-host');
$db_name = $input->getOption('db-name');
$db_port = $input->getOption('db-port');
$db_user = $input->getOption('db-user');
$db_hostport = $db_host . (!empty($db_port) ? ':' . $db_port : '');
$default_language = $input->getOption('default-language');
$force = $input->getOption('force');
if ($this->shouldSetDBConfig($input, $output)) {
if ($this->isDbAlreadyConfigured()
&& $this->isInputContainingConfigValues($input, $output)
&& !$input->getOption('reconfigure')) {
// Prevent overriding of existing DB when input contains configuration values and
// --reconfigure option is not used.
$output->writeln(
'<error>' . __('Database configuration already exists. Use --reconfigure option to override existing configuration.') . '</error>'
);
return self::ERROR_DB_CONFIG_ALREADY_SET;
}
if (!$this->isDbAlreadyConfigured() || $input->getOption('reconfigure')) {
$result = $this->configureDatabase($input, $output);
if (self::ABORTED_BY_USER === $result) {
......@@ -120,6 +144,13 @@ class InstallCommand extends AbstractConfigureCommand {
return $result; // Fail with error code
}
$db_host = $input->getOption('db-host');
$db_port = $input->getOption('db-port');
$db_hostport = $db_host . (!empty($db_port) ? ':' . $db_port : '');
$db_name = $input->getOption('db-name');
$db_user = $input->getOption('db-user');
$db_pass = $input->getOption('db-password');
if ($DB instanceof DB) {
// If global $DB is set at this point, it means that configuration file has been loaded
// prior to reconfiguration.
......@@ -132,6 +163,41 @@ class InstallCommand extends AbstractConfigureCommand {
$DB->clearSchemaCache();
$DB->connect();
}
} else {
// Ask to confirm installation based on existing configuration.
$run = $this->askForDbConfigConfirmation(
$input,
$output,
$DB->dbhost,
$DB->dbdefault,
$DB->dbuser
);
if (!$run) {
$output->writeln(
'<comment>' . __('Installation aborted.') . '</comment>',
OutputInterface::VERBOSITY_VERBOSE
);
return 0;
}
// $DB->dbhost can be array when using round robin feature
$hostport = explode(':', is_array($DB->dbhost) ? $DB->dbhost[0] : $DB->dbhost);
$db_host = $hostport[0];
if (count($hostport) < 2) {
// Host only case
$db_port = null;
} else if (intval($hostport[1]) > 0) {
// Host:port case
$db_port = $hostport[1];
} else {
// :Socket case
// TODO Handle socket connection
throw new \UnexpectedValueException('DB connection through socket is not yet handled in installation command');
}
$db_name = $DB->dbdefault;
$db_user = $DB->dbuser;
$db_pass = rawurldecode($DB->dbpassword); //rawurldecode as in DBmysql::connect()
}
$mysqli = new \mysqli();
......@@ -212,4 +278,33 @@ class InstallCommand extends AbstractConfigureCommand {
return $input->getOption('reconfigure') || !file_exists(GLPI_CONFIG_DIR . '/config_db.php');
}
/**
* Check if input contains DB config options.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return boolean
*/
private function isInputContainingConfigValues(InputInterface $input, OutputInterface $output) {
$config_options = [
'db-host',
'db-port',
'db-name',
'db-user',
'db-password',
];
foreach ($config_options as $option) {
$default_value = $this->getDefinition()->getOption($option)->getDefault();
$input_value = $input->getOption($option);
if ($default_value !== $input_value) {
return true;
}
}
return false;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment