1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/doc/lib/pgBackRestDoc/Custom/DocConfigData.pm
David Steele 44da314adb
Add compress-level range checking for each compress-type.
The prior range checking was done based on the valid values for gz. While this worked it was a subset of what is available for lz4 and zst.

Allow the range to be specified for each compress-type. Adding this functionality to the parse module would be a better solution but that is a bigger project than this fix deserves, at least for now.
2022-12-27 20:05:08 +07:00

558 lines
25 KiB
Perl

####################################################################################################################################
# Configuration Definition Data
#
# The configuration is defined in src/build/config/config.yaml, which also contains the documentation.
####################################################################################################################################
package pgBackRestDoc::Custom::DocConfigData;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Cwd qw(abs_path);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname basename);
use Getopt::Long qw(GetOptions);
use Storable qw(dclone);
use pgBackRestDoc::Common::Exception;
use pgBackRestDoc::Common::Log;
use pgBackRestDoc::ProjectInfo;
use pgBackRestTest::Common::Wait;
####################################################################################################################################
# Command constants
####################################################################################################################################
use constant CFGCMD_BACKUP => 'backup';
push @EXPORT, qw(CFGCMD_BACKUP);
use constant CFGCMD_HELP => 'help';
push @EXPORT, qw(CFGCMD_HELP);
use constant CFGCMD_INFO => 'info';
push @EXPORT, qw(CFGCMD_INFO);
use constant CFGCMD_VERSION => 'version';
####################################################################################################################################
# Command role constants - roles allowed for each command. Commands may have multiple processes that work together to implement
# their functionality. These roles allow each process to know what it is supposed to do.
####################################################################################################################################
# Called directly by the user. This is the main process of the command that may or may not spawn other command roles.
use constant CFGCMD_ROLE_MAIN => 'main';
push @EXPORT, qw(CFGCMD_ROLE_MAIN);
# Async worker that is spawned so the main process can return a result while work continues. An async worker may spawn local or
# remote workers.
use constant CFGCMD_ROLE_ASYNC => 'async';
push @EXPORT, qw(CFGCMD_ROLE_ASYNC);
# Local worker for parallelizing jobs. A local work may spawn a remote worker.
use constant CFGCMD_ROLE_LOCAL => 'local';
push @EXPORT, qw(CFGCMD_ROLE_LOCAL);
# Remote worker for accessing resources on another host
use constant CFGCMD_ROLE_REMOTE => 'remote';
push @EXPORT, qw(CFGCMD_ROLE_REMOTE);
####################################################################################################################################
# Option constants - options that are allowed for commands
####################################################################################################################################
# Command-line only options
#-----------------------------------------------------------------------------------------------------------------------------------
use constant CFGOPT_CONFIG => 'config';
push @EXPORT, qw(CFGOPT_CONFIG);
use constant CFGOPT_STANZA => 'stanza';
push @EXPORT, qw(CFGOPT_STANZA);
# Command-line only local/remote options
#-----------------------------------------------------------------------------------------------------------------------------------
# Paths
use constant CFGOPT_LOCK_PATH => 'lock-path';
push @EXPORT, qw(CFGOPT_LOCK_PATH);
use constant CFGOPT_LOG_PATH => 'log-path';
push @EXPORT, qw(CFGOPT_LOG_PATH);
use constant CFGOPT_SPOOL_PATH => 'spool-path';
push @EXPORT, qw(CFGOPT_SPOOL_PATH);
# Logging
use constant CFGOPT_LOG_LEVEL_STDERR => 'log-level-stderr';
push @EXPORT, qw(CFGOPT_LOG_LEVEL_STDERR);
use constant CFGOPT_LOG_TIMESTAMP => 'log-timestamp';
push @EXPORT, qw(CFGOPT_LOG_TIMESTAMP);
# Repository options
#-----------------------------------------------------------------------------------------------------------------------------------
# Prefix that must be used by all repo options that allow multiple configurations
use constant CFGDEF_PREFIX_REPO => 'repo';
# Repository General
use constant CFGOPT_REPO_PATH => CFGDEF_PREFIX_REPO . '-path';
push @EXPORT, qw(CFGOPT_REPO_PATH);
# Repository Host
use constant CFGOPT_REPO_HOST => CFGDEF_PREFIX_REPO . '-host';
use constant CFGOPT_REPO_HOST_CMD => CFGOPT_REPO_HOST . '-cmd';
push @EXPORT, qw(CFGOPT_REPO_HOST_CMD);
# Stanza options
#-----------------------------------------------------------------------------------------------------------------------------------
# Determines how many databases can be configured
use constant CFGDEF_INDEX_PG => 8;
push @EXPORT, qw(CFGDEF_INDEX_PG);
# Prefix that must be used by all db options that allow multiple configurations
use constant CFGDEF_PREFIX_PG => 'pg';
push @EXPORT, qw(CFGDEF_PREFIX_PG);
# Set default PostgreSQL cluster
use constant CFGOPT_PG_HOST => CFGDEF_PREFIX_PG . '-host';
use constant CFGOPT_PG_HOST_CMD => CFGOPT_PG_HOST . '-cmd';
push @EXPORT, qw(CFGOPT_PG_HOST_CMD);
####################################################################################################################################
# Option definition constants - defines, types, sections, etc.
####################################################################################################################################
# Command defines
#-----------------------------------------------------------------------------------------------------------------------------------
use constant CFGDEF_LOG_FILE => 'log-file';
push @EXPORT, qw(CFGDEF_LOG_FILE);
use constant CFGDEF_LOG_LEVEL_DEFAULT => 'log-level-default';
push @EXPORT, qw(CFGDEF_LOG_LEVEL_DEFAULT);
use constant CFGDEF_LOCK_REQUIRED => 'lock-required';
push @EXPORT, qw(CFGDEF_LOCK_REQUIRED);
use constant CFGDEF_LOCK_REMOTE_REQUIRED => 'lock-remote-required';
push @EXPORT, qw(CFGDEF_LOCK_REMOTE_REQUIRED);
use constant CFGDEF_LOCK_TYPE => 'lock-type';
push @EXPORT, qw(CFGDEF_LOCK_TYPE);
use constant CFGDEF_LOCK_TYPE_NONE => 'none';
use constant CFGDEF_PARAMETER_ALLOWED => 'parameter-allowed';
push @EXPORT, qw(CFGDEF_PARAMETER_ALLOWED);
# Option defines
#-----------------------------------------------------------------------------------------------------------------------------------
use constant CFGDEF_ALLOW_LIST => 'allow-list';
push @EXPORT, qw(CFGDEF_ALLOW_LIST);
use constant CFGDEF_ALLOW_RANGE => 'allow-range';
push @EXPORT, qw(CFGDEF_ALLOW_RANGE);
use constant CFGDEF_DEFAULT => 'default';
push @EXPORT, qw(CFGDEF_DEFAULT);
use constant CFGDEF_DEFAULT_LITERAL => 'default-literal';
push @EXPORT, qw(CFGDEF_DEFAULT_LITERAL);
# Group options together to share common configuration
use constant CFGDEF_GROUP => 'group';
push @EXPORT, qw(CFGDEF_GROUP);
use constant CFGDEF_INDEX => 'index';
push @EXPORT, qw(CFGDEF_INDEX);
use constant CFGDEF_INHERIT => 'inherit';
push @EXPORT, qw(CFGDEF_INHERIT);
use constant CFGDEF_INTERNAL => 'internal';
push @EXPORT, qw(CFGDEF_INTERNAL);
use constant CFGDEF_DEPRECATE => 'deprecate';
push @EXPORT, qw(CFGDEF_DEPRECATE);
use constant CFGDEF_NEGATE => 'negate';
push @EXPORT, qw(CFGDEF_NEGATE);
use constant CFGDEF_COMMAND => 'command';
push @EXPORT, qw(CFGDEF_COMMAND);
use constant CFGDEF_COMMAND_ROLE => 'command-role';
push @EXPORT, qw(CFGDEF_COMMAND_ROLE);
use constant CFGDEF_REQUIRED => 'required';
push @EXPORT, qw(CFGDEF_REQUIRED);
use constant CFGDEF_RESET => 'reset';
push @EXPORT, qw(CFGDEF_RESET);
use constant CFGDEF_SECTION => 'section';
push @EXPORT, qw(CFGDEF_SECTION);
use constant CFGDEF_SECURE => 'secure';
push @EXPORT, qw(CFGDEF_SECURE);
use constant CFGDEF_TYPE => 'type';
push @EXPORT, qw(CFGDEF_TYPE);
# Option types
#-----------------------------------------------------------------------------------------------------------------------------------
use constant CFGDEF_TYPE_BOOLEAN => 'boolean';
push @EXPORT, qw(CFGDEF_TYPE_BOOLEAN);
use constant CFGDEF_TYPE_HASH => 'hash';
push @EXPORT, qw(CFGDEF_TYPE_HASH);
use constant CFGDEF_TYPE_INTEGER => 'integer';
push @EXPORT, qw(CFGDEF_TYPE_INTEGER);
use constant CFGDEF_TYPE_LIST => 'list';
push @EXPORT, qw(CFGDEF_TYPE_LIST);
use constant CFGDEF_TYPE_PATH => 'path';
push @EXPORT, qw(CFGDEF_TYPE_PATH);
use constant CFGDEF_TYPE_STRING => 'string';
push @EXPORT, qw(CFGDEF_TYPE_STRING);
use constant CFGDEF_TYPE_SIZE => 'size';
push @EXPORT, qw(CFGDEF_TYPE_SIZE);
use constant CFGDEF_TYPE_TIME => 'time';
push @EXPORT, qw(CFGDEF_TYPE_TIME);
# Option config sections
#-----------------------------------------------------------------------------------------------------------------------------------
use constant CFGDEF_SECTION_GLOBAL => 'global';
push @EXPORT, qw(CFGDEF_SECTION_GLOBAL);
use constant CFGDEF_SECTION_STANZA => 'stanza';
push @EXPORT, qw(CFGDEF_SECTION_STANZA);
####################################################################################################################################
# Load configuration
####################################################################################################################################
use YAML::XS qw(LoadFile);
# Required so booleans are not read-only
local $YAML::XS::Boolean = "JSON::PP";
my $rhConfig = LoadFile(dirname(dirname($0)) . '/src/build/config/config.yaml');
my $rhCommandDefine = $rhConfig->{'command'};
my $rhOptionGroupDefine = $rhConfig->{'optionGroup'};
my $rhConfigDefine = $rhConfig->{'option'};
####################################################################################################################################
# Fix errors introduced by YAML::XS::LoadFile. This is typically fixed by setting local $YAML::XS::Boolean = "JSON::PP", but older
# Debian/Ubuntu versions do not support this fix. Some booleans get set read only and others also end up as empty strings. There is
# no apparent pattern to what gets broken so it is important to be on the lookout for strange output when adding new options.
#
# ??? For now this code is commented out since packages for older Debians can be built using backports. It is being preserved just
# in case it is needed before the migration to C is complete.
####################################################################################################################################
# sub optionDefineFixup
# {
# my $strKey = shift;
# my $rhDefine = shift;
#
# # Fix read-only required values so they are writable
# if (defined($rhDefine->{&CFGDEF_REQUIRED}))
# {
# my $value = $rhDefine->{&CFGDEF_REQUIRED} ? true : false;
# delete($rhDefine->{&CFGDEF_REQUIRED});
# $rhDefine->{&CFGDEF_REQUIRED} = $value;
# }
#
# # If the default is an empty string set to false. This must be a mangled boolean since empty strings are not valid defaults.
# if (defined($rhDefine->{&CFGDEF_DEFAULT}) && $rhDefine->{&CFGDEF_DEFAULT} eq '')
# {
# delete($rhDefine->{&CFGDEF_DEFAULT});
# $rhDefine->{&CFGDEF_DEFAULT} = false;
# }
# }
#
# # Fix all options
# foreach my $strKey (sort(keys(%{$rhConfigDefine})))
# {
# my $rhOption = $rhConfigDefine->{$strKey};
# optionDefineFixup($strKey, $rhOption);
#
# # Fix all option commands
# if (ref($rhOption->{&CFGDEF_COMMAND}))
# {
# foreach my $strCommand (sort(keys(%{$rhOption->{&CFGDEF_COMMAND}})))
# {
# optionDefineFixup("$strKey-$strCommand", $rhOption->{&CFGDEF_COMMAND}{$strCommand});
# }
# }
# }
####################################################################################################################################
# Process command define defaults
####################################################################################################################################
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
{
# Commands are external by default
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_INTERNAL}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_INTERNAL} = false;
}
# Log files are created by default
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_LOG_FILE}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_LOG_FILE} = true;
}
# Default log level is INFO
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_LOG_LEVEL_DEFAULT}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_LOG_LEVEL_DEFAULT} = INFO;
}
# Default lock required is false
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REQUIRED}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REQUIRED} = false;
}
# Default lock remote required is false
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REMOTE_REQUIRED}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REMOTE_REQUIRED} = false;
}
# Lock type must be set if a lock is required
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_TYPE}))
{
# Is a lock type required?
if ($rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REQUIRED})
{
confess &log(ERROR, "lock type is required for command '${strCommand}'");
}
$rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_TYPE} = CFGDEF_LOCK_TYPE_NONE;
}
else
{
if ($rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_REQUIRED} &&
$rhCommandDefine->{$strCommand}{&CFGDEF_LOCK_TYPE} eq CFGDEF_LOCK_TYPE_NONE)
{
confess &log(ERROR, "lock type is required for command '${strCommand}' and cannot be 'none'");
}
}
# Default parameter allowed is false
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_PARAMETER_ALLOWED}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_PARAMETER_ALLOWED} = false;
}
# All commands have the default role
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_COMMAND_ROLE}{&CFGCMD_ROLE_MAIN}))
{
$rhCommandDefine->{$strCommand}{&CFGDEF_COMMAND_ROLE}{&CFGCMD_ROLE_MAIN} = {};
}
}
####################################################################################################################################
# Process option define defaults
####################################################################################################################################
foreach my $strKey (sort(keys(%{$rhConfigDefine})))
{
my $rhOption = $rhConfigDefine->{$strKey};
# If the define is a scalar then copy the entire define from the referenced option
if (defined($rhConfigDefine->{$strKey}{&CFGDEF_INHERIT}))
{
# Make a copy in case there are overrides that need to be applied after inheriting
my $hConfigDefineOverride = dclone($rhConfigDefine->{$strKey});
# Copy the option being inherited from
$rhConfigDefine->{$strKey} = dclone($rhConfigDefine->{$rhConfigDefine->{$strKey}{&CFGDEF_INHERIT}});
# No need to copy the inheritance key
delete($rhConfigDefine->{$strKey}{&CFGDEF_INHERIT});
# It makes no sense to inherit deprecations - they must be specified for each option
delete($rhConfigDefine->{$strKey}{&CFGDEF_DEPRECATE});
# Apply overrides
foreach my $strOptionDef (sort(keys(%{$hConfigDefineOverride})))
{
$rhConfigDefine->{$strKey}{$strOptionDef} = $hConfigDefineOverride->{$strOptionDef};
}
# Update option variable with new hash reference
$rhOption = $rhConfigDefine->{$strKey}
}
# If command is not specified then the option is valid for all commands except version and help
if (!defined($rhOption->{&CFGDEF_COMMAND}))
{
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
{
next if $strCommand eq CFGCMD_HELP || $strCommand eq CFGCMD_VERSION;
$rhOption->{&CFGDEF_COMMAND}{$strCommand} = {};
}
}
# Else if the command section is a scalar then copy the section from the referenced option
elsif (defined($rhConfigDefine->{$strKey}{&CFGDEF_COMMAND}) && !ref($rhConfigDefine->{$strKey}{&CFGDEF_COMMAND}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_COMMAND} =
dclone($rhConfigDefine->{$rhConfigDefine->{$strKey}{&CFGDEF_COMMAND}}{&CFGDEF_COMMAND});
}
# If the allow list is a scalar then copy the list from the referenced option
if (defined($rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_LIST}) && !ref($rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_LIST}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_LIST} =
dclone($rhConfigDefine->{$rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_LIST}}{&CFGDEF_ALLOW_LIST});
}
# Default type is string
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_TYPE}))
{
&log(ASSERT, "type is required for option '${strKey}'");
}
# Default required is true
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_REQUIRED}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_REQUIRED} = true;
}
# Default internal is false
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_INTERNAL}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_INTERNAL} = false;
}
# All boolean config options can be negated. Boolean command-line options must be marked for negation individually.
if ($rhConfigDefine->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_BOOLEAN && defined($rhConfigDefine->{$strKey}{&CFGDEF_SECTION}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_NEGATE} = true;
}
# Default for negation is false
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_NEGATE}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_NEGATE} = false;
}
# All config options can be reset
if (defined($rhConfigDefine->{$strKey}{&CFGDEF_SECTION}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_RESET} = true;
}
elsif (!defined($rhConfigDefine->{$strKey}{&CFGDEF_RESET}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_RESET} = false;
}
# By default options are not secure
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_SECURE}))
{
$rhConfigDefine->{$strKey}{&CFGDEF_SECURE} = false;
}
# All size and time options must have an allow range
if (($rhConfigDefine->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_TIME ||
$rhConfigDefine->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_SIZE) &&
!(defined($rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_RANGE}) || defined($rhConfigDefine->{$strKey}{&CFGDEF_ALLOW_LIST})))
{
confess &log(ASSERT, "int/size/time option '${strKey}' must have allow range or list");
}
# Ensure all commands are valid
foreach my $strCommand (sort(keys(%{$rhConfigDefine->{$strKey}{&CFGDEF_COMMAND}})))
{
if (!defined($rhCommandDefine->{$strCommand}))
{
confess &log(ASSERT, "invalid command '${strCommand}'");
}
}
}
# Generate valid command roles for each option
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $rhOption = $rhConfigDefine->{$strOption};
# Generate valid command roles for each command in the option
foreach my $strCommand (sort(keys(%{$rhOption->{&CFGDEF_COMMAND}})))
{
# If command roles are defined in the option command override then check that they are valid
if (defined($rhOption->{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_COMMAND_ROLE}))
{
foreach my $strCommandRole (sort(keys(%{$rhOption->{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_COMMAND_ROLE}})))
{
if (!defined($rhCommandDefine->{$strCommand}{&CFGDEF_COMMAND_ROLE}{$strCommandRole}))
{
confess &log(
ASSERT, "option '${strOption}', command '${strCommand}' has invalid command role '${strCommandRole}'");
}
}
}
# Else if the option has command roles defined then use the intersection of command roles with the command
elsif (defined($rhOption->{&CFGDEF_COMMAND_ROLE}))
{
foreach my $strCommandRole (sort(keys(%{$rhOption->{&CFGDEF_COMMAND_ROLE}})))
{
if (defined($rhCommandDefine->{$strCommand}{&CFGDEF_COMMAND_ROLE}{$strCommandRole}))
{
$rhOption->{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_COMMAND_ROLE}{$strCommandRole} = {};
}
}
}
# Else copy the command roles from the command
else
{
foreach my $strCommandRole (sort(keys(%{$rhCommandDefine->{$strCommand}{&CFGDEF_COMMAND_ROLE}})))
{
$rhOption->{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_COMMAND_ROLE}{$strCommandRole} = {};
}
}
}
# Remove option command roles so they don't accidentally get used in processing (since they were copied to option commands)
delete($rhOption->{&CFGDEF_COMMAND_ROLE});
}
####################################################################################################################################
# Get option definition
####################################################################################################################################
sub cfgDefine
{
return dclone($rhConfigDefine);
}
push @EXPORT, qw(cfgDefine);
####################################################################################################################################
# Get command definition
####################################################################################################################################
sub cfgDefineCommand
{
return dclone($rhCommandDefine);
}
push @EXPORT, qw(cfgDefineCommand);
####################################################################################################################################
# Get option group definition
####################################################################################################################################
sub cfgDefineOptionGroup
{
return dclone($rhOptionGroupDefine);
}
push @EXPORT, qw(cfgDefineOptionGroup);
####################################################################################################################################
# Get list of all commands
####################################################################################################################################
sub cfgDefineCommandList
{
# Return sorted list
return (sort(keys(%{$rhCommandDefine})));
}
push @EXPORT, qw(cfgDefineCommandList);
####################################################################################################################################
# Get list of all option types
####################################################################################################################################
sub cfgDefineOptionTypeList
{
my $rhOptionTypeMap;
# Get unique list of types
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $strOptionType = $rhConfigDefine->{$strOption}{&CFGDEF_TYPE};
if (!defined($rhOptionTypeMap->{$strOptionType}))
{
$rhOptionTypeMap->{$strOptionType} = true;
}
};
# Return sorted list
return (sort(keys(%{$rhOptionTypeMap})));
}
push @EXPORT, qw(cfgDefineOptionTypeList);
1;