Enable config:set to set variables inside a nested array of settings (#12772)

* Enable config:set to set variables inside a nested array of settings
Re-index arrays when forgetting a value from a sequential numerically indexed array

* cleanup
This commit is contained in:
Tony Murray 2021-04-26 21:03:03 -05:00 committed by GitHub
parent 7510224ead
commit ebadcbc8af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 101 additions and 7 deletions

View File

@ -4,6 +4,8 @@ namespace App\Console\Commands;
use App\Console\Commands\Traits\CompletesConfigArgument;
use App\Console\LnmsCommand;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use LibreNMS\Config;
use LibreNMS\DB\Eloquent;
use LibreNMS\Util\DynamicConfig;
@ -39,11 +41,15 @@ class SetConfigCommand extends LnmsCommand
$setting = $this->argument('setting');
$value = $this->argument('value');
$force = $this->option('ignore-checks');
$parent = null;
if (! $force && ! $definition->isValidSetting($setting)) {
$this->error(trans('commands.config:set.errors.invalid'));
if (! $definition->isValidSetting($setting)) {
$parent = $this->findParentSetting($definition, $setting);
if (! $force && ! $parent) {
$this->error(trans('commands.config:set.errors.invalid'));
return 2;
return 2;
}
}
if (! Eloquent::isConnected()) {
@ -53,16 +59,41 @@ class SetConfigCommand extends LnmsCommand
}
if (! $force && ! $value) {
if ($this->confirm(trans('commands.config:set.confirm', ['setting' => $setting]))) {
Config::erase($setting);
$message = $parent
? trans('commands.config:set.forget_from', ['path' => $this->getChildPath($setting, $parent), 'parent' => $parent])
: trans('commands.config:set.confirm', ['setting' => $setting]);
return 0;
if ($this->confirm($message)) {
return $this->erase($setting, $parent) ? 0 : 1;
}
return 3;
}
$value = $this->juggleType($value);
// handle appending to arrays
if (Str::endsWith($setting, '.+')) {
$setting = substr($setting, 0, -2);
$sub_data = Config::get($setting, []);
if (! is_array($sub_data)) {
$this->error(trans('commands.config:set.errors.append'));
return 2;
}
array_push($sub_data, $value);
$value = $sub_data;
}
// handle setting value inside multi-dimensional array
if ($parent) {
$parent_data = Config::get($parent);
Arr::set($parent_data, $this->getChildPath($setting, $parent), $value);
$value = $parent_data;
$setting = $parent;
}
$configItem = $definition->get($setting);
if (! $force && ! $configItem->checkValue($value)) {
$message = ($configItem->type || $configItem->validate)
@ -93,4 +124,65 @@ class SetConfigCommand extends LnmsCommand
return json_last_error() ? $value : $json;
}
private function findParentSetting(DynamicConfig $definition, $setting): ?string
{
$parts = explode('.', $setting);
array_pop($parts); // looking for parent, not this setting
while (! empty($parts)) {
$name = implode('.', $parts);
if ($definition->isValidSetting($name)) {
return $name;
}
array_pop($parts);
}
return null;
}
private function erase($setting, $parent = null)
{
if ($parent) {
$data = Config::get($parent);
if (preg_match("/^$parent\.?(?<sub>.+)\\.(?<index>\\d+)\$/", $setting, $matches)) {
// nested inside the parent setting, update just the required part
$sub_data = Arr::get($data, $matches['sub']);
$this->forgetWithIndex($sub_data, $matches['index']);
Arr::set($data, $matches['sub'], $sub_data);
} else {
// not nested, just forget the setting
$this->forgetWithIndex($data, $this->getChildPath($setting, $parent));
}
return Config::persist($parent, $data);
}
return Config::erase($setting);
}
private function getChildPath($setting, $parent = null): string
{
return ltrim(Str::after($setting, $parent), '.');
}
private function hasSequentialIndex($array): bool
{
if (! is_array($array) || $array === []) {
return false;
}
return array_keys($array) === range(0, count($array) - 1);
}
private function forgetWithIndex(&$data, $matches)
{
// detect sequentially numeric indexed array so we can re-index the array
if ($this->hasSequentialIndex($data)) {
array_splice($data, (int) $matches, 1);
} else {
Arr::forget($data, $matches);
}
}
}

View File

@ -13,14 +13,16 @@ return [
'config:set' => [
'description' => 'Set configuration value (or unset)',
'arguments' => [
'setting' => 'setting to set in dot notation (example: snmp.community.0)',
'setting' => 'setting to set in dot notation (example: snmp.community.0) To append to an array suffix with .+',
'value' => 'value to set, unset setting if this is omitted',
],
'options' => [
'ignore-checks' => 'Ignore all safety checks',
],
'confirm' => 'Reset :setting to the default?',
'forget_from' => 'Forget :path from :parent?',
'errors' => [
'append' => 'Cannot append to non-array setting',
'failed' => 'Failed to set :setting',
'invalid' => 'This is not a valid setting. Please check your spelling',
'nodb' => 'Database is not connected',