You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-15 01:04:37 +02:00
Increase max index allowed for pg/repo options to 256.
The prior limitations were based on using getopt_long() to parse command-line options, which required a static list of allowed options. Setting index max too high bloated the binary unacceptably. 45a4e80
replaced the functionality of getopt_long() but the static list remained.
Improve cfgParseOption() to use available option data and remove the need for a static list. This also allows the option deprecations to be represented more compactly.
Index max is still capped at 256 because a large enough index could cause parseOptionIdxValue() to run out of memory since it allocates a static list based on the highest index found. If that function were improved with a map of found index values then index max could be set to UINT64_MAX.
Note that deprecations no longer set an index max or define whether reset is valid. These were space-saving measures which are no longer required. This means that indexed deprecated options will also be valid up to 256 and always allow reset, but it doesn't seem worth additional code to limit this behavior.
cfgParseOptionId() is no longer needed because calling cfgParseOption() with .ignoreMissingIndex = true duplicates the functionality of cfgParseOptionId(). This leads to some simplification in the help code.
This commit is contained in:
@ -149,8 +149,6 @@ use constant CFGDEF_GROUP => 'group';
|
||||
|
||||
use constant CFGDEF_INDEX => 'index';
|
||||
push @EXPORT, qw(CFGDEF_INDEX);
|
||||
use constant CFGDEF_INDEX_TOTAL => 'indexTotal';
|
||||
push @EXPORT, qw(CFGDEF_INDEX_TOTAL);
|
||||
use constant CFGDEF_INHERIT => 'inherit';
|
||||
push @EXPORT, qw(CFGDEF_INHERIT);
|
||||
use constant CFGDEF_INTERNAL => 'internal';
|
||||
@ -159,8 +157,6 @@ use constant CFGDEF_DEPRECATE => 'deprecat
|
||||
push @EXPORT, qw(CFGDEF_DEPRECATE);
|
||||
use constant CFGDEF_NEGATE => 'negate';
|
||||
push @EXPORT, qw(CFGDEF_NEGATE);
|
||||
use constant CFGDEF_PREFIX => 'prefix';
|
||||
push @EXPORT, qw(CFGDEF_PREFIX);
|
||||
use constant CFGDEF_COMMAND => 'command';
|
||||
push @EXPORT, qw(CFGDEF_COMMAND);
|
||||
use constant CFGDEF_COMMAND_ROLE => 'command-role';
|
||||
@ -328,22 +324,6 @@ foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Process option group defaults
|
||||
####################################################################################################################################
|
||||
foreach my $strGroup (sort(keys(%{$rhOptionGroupDefine})))
|
||||
{
|
||||
# Error if prefix and index total are not both defined
|
||||
if ((defined($rhOptionGroupDefine->{$strGroup}{&CFGDEF_PREFIX}) &&
|
||||
!defined($rhOptionGroupDefine->{$strGroup}{&CFGDEF_INDEX_TOTAL})) ||
|
||||
(!defined($rhOptionGroupDefine->{$strGroup}{&CFGDEF_PREFIX}) &&
|
||||
defined($rhOptionGroupDefine->{$strGroup}{&CFGDEF_INDEX_TOTAL})))
|
||||
{
|
||||
confess &log(
|
||||
ASSERT, "CFGDEF_PREFIX and CFGDEF_INDEX_TOTAL must both be defined (or neither) for option group '${strGroup}'");
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# Process option define defaults
|
||||
####################################################################################################################################
|
||||
@ -351,17 +331,6 @@ foreach my $strKey (sort(keys(%{$rhConfigDefine})))
|
||||
{
|
||||
my $rhOption = $rhConfigDefine->{$strKey};
|
||||
|
||||
# Error on invalid configuration
|
||||
if (defined($rhOption->{&CFGDEF_INDEX_TOTAL}))
|
||||
{
|
||||
confess &log(ASSERT, "CFGDEF_INDEX_TOTAL cannot be defined for option '${strKey}'");
|
||||
}
|
||||
|
||||
if (defined($rhOption->{&CFGDEF_PREFIX}))
|
||||
{
|
||||
confess &log(ASSERT, "CFGDEF_PREFIX cannot be defined for option '${strKey}'");
|
||||
}
|
||||
|
||||
# If the define is a scalar then copy the entire define from the referenced option
|
||||
if (defined($rhConfigDefine->{$strKey}{&CFGDEF_INHERIT}))
|
||||
{
|
||||
@ -387,15 +356,6 @@ foreach my $strKey (sort(keys(%{$rhConfigDefine})))
|
||||
$rhOption = $rhConfigDefine->{$strKey}
|
||||
}
|
||||
|
||||
# If the option group is defined then copy configuration from the group to the option
|
||||
if (defined($rhOption->{&CFGDEF_GROUP}))
|
||||
{
|
||||
my $rhGroup = $rhOptionGroupDefine->{$rhConfigDefine->{$strKey}{&CFGDEF_GROUP}};
|
||||
|
||||
$rhOption->{&CFGDEF_INDEX_TOTAL} = $rhGroup->{&CFGDEF_INDEX_TOTAL};
|
||||
$rhOption->{&CFGDEF_PREFIX} = $rhGroup->{&CFGDEF_PREFIX};
|
||||
}
|
||||
|
||||
# If command is not specified then the option is valid for all commands except version and help
|
||||
if (!defined($rhOption->{&CFGDEF_COMMAND}))
|
||||
{
|
||||
@ -438,12 +398,6 @@ foreach my $strKey (sort(keys(%{$rhConfigDefine})))
|
||||
$rhConfigDefine->{$strKey}{&CFGDEF_INTERNAL} = false;
|
||||
}
|
||||
|
||||
# Set index total for any option where it has not been explicitly defined
|
||||
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_INDEX_TOTAL}))
|
||||
{
|
||||
$rhConfigDefine->{$strKey}{&CFGDEF_INDEX_TOTAL} = 1;
|
||||
}
|
||||
|
||||
# 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}))
|
||||
{
|
||||
@ -472,12 +426,6 @@ foreach my $strKey (sort(keys(%{$rhConfigDefine})))
|
||||
$rhConfigDefine->{$strKey}{&CFGDEF_SECURE} = false;
|
||||
}
|
||||
|
||||
# Set all indices to 1 by default - this defines how many copies of any option there can be
|
||||
if (!defined($rhConfigDefine->{$strKey}{&CFGDEF_INDEX_TOTAL}))
|
||||
{
|
||||
$rhConfigDefine->{$strKey}{&CFGDEF_INDEX_TOTAL} = 1;
|
||||
}
|
||||
|
||||
# All int, size and time options must have an allow range
|
||||
if (($rhConfigDefine->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_INTEGER ||
|
||||
$rhConfigDefine->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_TIME ||
|
||||
|
@ -855,7 +855,7 @@ sub helpOptionGet
|
||||
# Get the example
|
||||
my $strExample;
|
||||
|
||||
my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_PREFIX};
|
||||
my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_GROUP};
|
||||
my $strOptionIndex = defined($strOptionPrefix) ?
|
||||
"${strOptionPrefix}1-" . substr($strOption, length($strOptionPrefix) + 1) : $strOption;
|
||||
|
||||
|
@ -33,43 +33,6 @@ use pgBackRestDoc::ProjectInfo;
|
||||
####################################################################################################################################
|
||||
use constant DOC_USER => getpwuid($UID) eq 'root' ? 'ubuntu' : getpwuid($UID) . '';
|
||||
|
||||
####################################################################################################################################
|
||||
# Generate indexed defines
|
||||
####################################################################################################################################
|
||||
my $rhConfigDefineIndex = cfgDefine();
|
||||
|
||||
foreach my $strKey (sort(keys(%{$rhConfigDefineIndex})))
|
||||
{
|
||||
# Build options for all possible db configurations
|
||||
if (defined($rhConfigDefineIndex->{$strKey}{&CFGDEF_PREFIX}) &&
|
||||
$rhConfigDefineIndex->{$strKey}{&CFGDEF_PREFIX} eq CFGDEF_PREFIX_PG)
|
||||
{
|
||||
my $strPrefix = $rhConfigDefineIndex->{$strKey}{&CFGDEF_PREFIX};
|
||||
|
||||
for (my $iIndex = 1; $iIndex <= CFGDEF_INDEX_PG; $iIndex++)
|
||||
{
|
||||
my $strKeyNew = "${strPrefix}${iIndex}" . substr($strKey, length($strPrefix));
|
||||
|
||||
$rhConfigDefineIndex->{$strKeyNew} = dclone($rhConfigDefineIndex->{$strKey});
|
||||
|
||||
$rhConfigDefineIndex->{$strKeyNew}{&CFGDEF_INDEX_TOTAL} = CFGDEF_INDEX_PG;
|
||||
$rhConfigDefineIndex->{$strKeyNew}{&CFGDEF_INDEX} = $iIndex - 1;
|
||||
|
||||
# Options indexed > 1 are never required
|
||||
if ($iIndex != 1)
|
||||
{
|
||||
$rhConfigDefineIndex->{$strKeyNew}{&CFGDEF_REQUIRED} = false;
|
||||
}
|
||||
}
|
||||
|
||||
delete($rhConfigDefineIndex->{$strKey});
|
||||
}
|
||||
else
|
||||
{
|
||||
$rhConfigDefineIndex->{$strKey}{&CFGDEF_INDEX} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
# CONSTRUCTOR
|
||||
####################################################################################################################################
|
||||
@ -563,17 +526,9 @@ sub backrestConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
# Make sure the specified option exists
|
||||
# ??? This is too simplistic to handle new indexed options. The check below works for now but it would be good
|
||||
# ??? to bring back more sophisticated checking in the future.
|
||||
# if (!defined($rhConfigDefineIndex->{$strKey}))
|
||||
# {
|
||||
# confess &log(ERROR, "option ${strKey} does not exist");
|
||||
# }
|
||||
|
||||
# If this option is a hash and the value is already set then append to the array
|
||||
if (defined($rhConfigDefineIndex->{$strKey}) &&
|
||||
$rhConfigDefineIndex->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_HASH &&
|
||||
if (defined(cfgDefine()->{$strKey}) &&
|
||||
cfgDefine()->{$strKey}{&CFGDEF_TYPE} eq CFGDEF_TYPE_HASH &&
|
||||
defined(${$self->{config}}{$strHostName}{$$hCacheKey{file}}{$strSection}{$strKey}))
|
||||
{
|
||||
my @oValue = ();
|
||||
|
@ -13,6 +13,20 @@
|
||||
|
||||
<release-list>
|
||||
<release date="XXXX-XX-XX" version="2.36dev" title="UNDER DEVELOPMENT">
|
||||
<release-core-list>
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
<github-pull-request id="1481"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="cynthia.shang"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Increase max index allowed for <id>pg</id>/<id>repo</id> options to 256.</p>
|
||||
</release-item>
|
||||
</release-improvement-list>
|
||||
</release-core-list>
|
||||
</release>
|
||||
|
||||
<release date="2021-08-23" version="2.35" title="Binary Protocol">
|
||||
|
@ -158,12 +158,8 @@ command:
|
||||
# the group.
|
||||
####################################################################################################################################
|
||||
optionGroup:
|
||||
pg:
|
||||
indexTotal: 8
|
||||
prefix: pg
|
||||
repo:
|
||||
indexTotal: 4
|
||||
prefix: repo
|
||||
pg: {}
|
||||
repo: {}
|
||||
|
||||
####################################################################################################################################
|
||||
# Options
|
||||
@ -1213,7 +1209,7 @@ option:
|
||||
type: integer
|
||||
internal: true
|
||||
required: false
|
||||
allow-range: [1, 8]
|
||||
allow-range: [1, 256]
|
||||
command:
|
||||
backup:
|
||||
command-role:
|
||||
@ -1271,8 +1267,8 @@ option:
|
||||
list:
|
||||
- false
|
||||
deprecate:
|
||||
db-host: {index: 1, reset: false}
|
||||
db?-host: {reset: false}
|
||||
db-host: {}
|
||||
db?-host: {}
|
||||
|
||||
pg-host-cmd:
|
||||
section: stanza
|
||||
@ -1292,8 +1288,8 @@ option:
|
||||
depend:
|
||||
option: pg-host
|
||||
deprecate:
|
||||
db-cmd: {index: 1, reset: false}
|
||||
db?-cmd: {reset: false}
|
||||
db-cmd: {}
|
||||
db?-cmd: {}
|
||||
|
||||
pg-host-config:
|
||||
inherit: pg-host-cmd
|
||||
@ -1301,8 +1297,8 @@ option:
|
||||
default-literal: true
|
||||
required: true
|
||||
deprecate:
|
||||
db-config: {index: 1, reset: false}
|
||||
db?-config: {reset: false}
|
||||
db-config: {}
|
||||
db?-config: {}
|
||||
|
||||
pg-host-config-include-path:
|
||||
inherit: pg-host-cmd
|
||||
@ -1322,16 +1318,16 @@ option:
|
||||
required: false
|
||||
allow-range: [0, 65535]
|
||||
deprecate:
|
||||
db-ssh-port: {index: 1, reset: false}
|
||||
db?-ssh-port: {reset: false}
|
||||
db-ssh-port: {}
|
||||
db?-ssh-port: {}
|
||||
|
||||
pg-host-user:
|
||||
inherit: pg-host-cmd
|
||||
default: postgres
|
||||
required: false
|
||||
deprecate:
|
||||
db-user: {index: 1, reset: false}
|
||||
db?-user: {reset: false}
|
||||
db-user: {}
|
||||
db?-user: {}
|
||||
|
||||
pg-path:
|
||||
section: stanza
|
||||
@ -1348,8 +1344,8 @@ option:
|
||||
stanza-delete: {}
|
||||
stanza-upgrade: {}
|
||||
deprecate:
|
||||
db-path: {index: 1, reset: false}
|
||||
db?-path: {reset: false}
|
||||
db-path: {}
|
||||
db?-path: {}
|
||||
|
||||
pg-port:
|
||||
section: stanza
|
||||
@ -1360,8 +1356,8 @@ option:
|
||||
command: pg-database
|
||||
depend: pg-database
|
||||
deprecate:
|
||||
db-port: {index: 1, reset: false}
|
||||
db?-port: {reset: false}
|
||||
db-port: {}
|
||||
db?-port: {}
|
||||
|
||||
pg-socket-path:
|
||||
inherit: pg-port
|
||||
@ -1369,8 +1365,8 @@ option:
|
||||
required: false
|
||||
default: ~
|
||||
deprecate:
|
||||
db-socket-path: {index: 1, reset: false}
|
||||
db?-socket-path: {reset: false}
|
||||
db-socket-path: {}
|
||||
db?-socket-path: {}
|
||||
|
||||
pg-user:
|
||||
section: stanza
|
||||
@ -1392,7 +1388,7 @@ option:
|
||||
repo:
|
||||
type: integer
|
||||
required: false
|
||||
allow-range: [1, 4]
|
||||
allow-range: [1, 256]
|
||||
command:
|
||||
archive-get:
|
||||
command-role:
|
||||
@ -1581,7 +1577,7 @@ option:
|
||||
local: {}
|
||||
remote: {}
|
||||
deprecate:
|
||||
repo-type: {index: 1, reset: false}
|
||||
repo-type: {}
|
||||
|
||||
repo-azure-account:
|
||||
section: global
|
||||
@ -1626,7 +1622,7 @@ option:
|
||||
- aes-256-cbc
|
||||
group: repo
|
||||
deprecate:
|
||||
repo-cipher-pass: {index: 1, reset: false}
|
||||
repo-cipher-pass: {}
|
||||
|
||||
repo-cipher-type:
|
||||
section: global
|
||||
@ -1638,7 +1634,7 @@ option:
|
||||
- aes-256-cbc
|
||||
command: repo-type
|
||||
deprecate:
|
||||
repo-cipher-type: {index: 1, reset: false}
|
||||
repo-cipher-type: {}
|
||||
|
||||
repo-gcs-bucket:
|
||||
section: global
|
||||
@ -1693,7 +1689,7 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
deprecate:
|
||||
hardlink: {index: 1, reset: false}
|
||||
hardlink: {}
|
||||
|
||||
repo-host:
|
||||
section: global
|
||||
@ -1710,7 +1706,7 @@ option:
|
||||
list:
|
||||
- false
|
||||
deprecate:
|
||||
backup-host: {index: 1, reset: false}
|
||||
backup-host: {}
|
||||
|
||||
repo-host-cmd:
|
||||
section: global
|
||||
@ -1739,7 +1735,7 @@ option:
|
||||
depend:
|
||||
option: repo-host
|
||||
deprecate:
|
||||
backup-cmd: {index: 1, reset: false}
|
||||
backup-cmd: {}
|
||||
|
||||
repo-host-config:
|
||||
section: global
|
||||
@ -1755,7 +1751,7 @@ option:
|
||||
depend:
|
||||
option: repo-host
|
||||
deprecate:
|
||||
backup-config: {index: 1, reset: false}
|
||||
backup-config: {}
|
||||
|
||||
repo-host-config-include-path:
|
||||
inherit: repo-host-config
|
||||
@ -1783,7 +1779,7 @@ option:
|
||||
depend:
|
||||
option: repo-host
|
||||
deprecate:
|
||||
backup-ssh-port: {index: 1, reset: false}
|
||||
backup-ssh-port: {}
|
||||
|
||||
repo-host-user:
|
||||
section: global
|
||||
@ -1799,7 +1795,7 @@ option:
|
||||
depend:
|
||||
option: repo-host
|
||||
deprecate:
|
||||
backup-user: {index: 1, reset: false}
|
||||
backup-user: {}
|
||||
|
||||
repo-path:
|
||||
section: global
|
||||
@ -1808,7 +1804,7 @@ option:
|
||||
default: /var/lib/pgbackrest
|
||||
command: repo-type
|
||||
deprecate:
|
||||
repo-path: {index: 1, reset: false}
|
||||
repo-path: {}
|
||||
|
||||
repo-retention-archive:
|
||||
section: global
|
||||
@ -1822,7 +1818,7 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
deprecate:
|
||||
retention-archive: {index: 1, reset: false}
|
||||
retention-archive: {}
|
||||
|
||||
repo-retention-archive-type:
|
||||
section: global
|
||||
@ -1839,7 +1835,7 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
deprecate:
|
||||
retention-archive-type: {index: 1, reset: false}
|
||||
retention-archive-type: {}
|
||||
|
||||
repo-retention-diff:
|
||||
section: global
|
||||
@ -1853,7 +1849,7 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
deprecate:
|
||||
retention-diff: {index: 1, reset: false}
|
||||
retention-diff: {}
|
||||
|
||||
repo-retention-full-type:
|
||||
section: global
|
||||
@ -1879,7 +1875,7 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
deprecate:
|
||||
retention-full: {index: 1, reset: false}
|
||||
retention-full: {}
|
||||
|
||||
repo-retention-history:
|
||||
section: global
|
||||
@ -1903,12 +1899,12 @@ option:
|
||||
list:
|
||||
- s3
|
||||
deprecate:
|
||||
repo-s3-bucket: {index: 1, reset: false}
|
||||
repo-s3-bucket: {}
|
||||
|
||||
repo-s3-endpoint:
|
||||
inherit: repo-s3-bucket
|
||||
deprecate:
|
||||
repo-s3-endpoint: {index: 1, reset: false}
|
||||
repo-s3-endpoint: {}
|
||||
|
||||
repo-s3-key:
|
||||
section: global
|
||||
@ -1921,12 +1917,12 @@ option:
|
||||
list:
|
||||
- shared
|
||||
deprecate:
|
||||
repo-s3-key: {index: 1, reset: false}
|
||||
repo-s3-key: {}
|
||||
|
||||
repo-s3-key-secret:
|
||||
inherit: repo-s3-key
|
||||
deprecate:
|
||||
repo-s3-key-secret: {index: 1, reset: false}
|
||||
repo-s3-key-secret: {}
|
||||
|
||||
repo-s3-key-type:
|
||||
inherit: repo-s3-bucket
|
||||
@ -1938,7 +1934,7 @@ option:
|
||||
repo-s3-region:
|
||||
inherit: repo-s3-bucket
|
||||
deprecate:
|
||||
repo-s3-region: {index: 1, reset: false}
|
||||
repo-s3-region: {}
|
||||
|
||||
repo-s3-role:
|
||||
inherit: repo-s3-bucket
|
||||
@ -1977,10 +1973,10 @@ option:
|
||||
- gcs
|
||||
- s3
|
||||
deprecate:
|
||||
repo-s3-verify-ssl: {index: 1, reset: false}
|
||||
repo?-azure-verify-tls: {index: 1}
|
||||
repo?-s3-verify-ssl: {index: 1, reset: false}
|
||||
repo?-s3-verify-tls: {index: 1}
|
||||
repo-s3-verify-ssl: {}
|
||||
repo?-azure-verify-tls: {}
|
||||
repo?-s3-verify-ssl: {}
|
||||
repo?-s3-verify-tls: {}
|
||||
|
||||
repo-storage-ca-file:
|
||||
section: global
|
||||
@ -1990,17 +1986,17 @@ option:
|
||||
command: repo-type
|
||||
depend: repo-storage-verify-tls
|
||||
deprecate:
|
||||
repo-s3-ca-file: {index: 1, reset: false}
|
||||
repo?-azure-ca-file: {index: 1}
|
||||
repo?-s3-ca-file: {index: 1}
|
||||
repo-s3-ca-file: {}
|
||||
repo?-azure-ca-file: {}
|
||||
repo?-s3-ca-file: {}
|
||||
|
||||
repo-storage-ca-path:
|
||||
inherit: repo-storage-ca-file
|
||||
type: path
|
||||
deprecate:
|
||||
repo-s3-ca-path: {index: 1, reset: false}
|
||||
repo?-azure-ca-path: {index: 1}
|
||||
repo?-s3-ca-path: {index: 1}
|
||||
repo-s3-ca-path: {}
|
||||
repo?-azure-ca-path: {}
|
||||
repo?-s3-ca-path: {}
|
||||
|
||||
repo-storage-host:
|
||||
section: global
|
||||
@ -2014,9 +2010,9 @@ option:
|
||||
- azure
|
||||
- s3
|
||||
deprecate:
|
||||
repo-s3-host: {index: 1, reset: false}
|
||||
repo?-azure-host: {index: 1}
|
||||
repo?-s3-host: {index: 1}
|
||||
repo-s3-host: {}
|
||||
repo?-azure-host: {}
|
||||
repo?-s3-host: {}
|
||||
|
||||
repo-storage-port:
|
||||
section: global
|
||||
@ -2027,5 +2023,5 @@ option:
|
||||
command: repo-type
|
||||
depend: repo-storage-host
|
||||
deprecate:
|
||||
repo?-azure-port: {index: 1}
|
||||
repo?-s3-port: {index: 1}
|
||||
repo?-azure-port: {}
|
||||
repo?-s3-port: {}
|
||||
|
@ -217,7 +217,6 @@ Parse option group list
|
||||
typedef struct BldCfgOptionGroupRaw
|
||||
{
|
||||
const String *const name; // See BldCfgOptionGroup for comments
|
||||
const Variant *indexTotal;
|
||||
} BldCfgOptionGroupRaw;
|
||||
|
||||
static List *
|
||||
@ -238,36 +237,11 @@ bldCfgParseOptionGroupList(Yaml *const yaml)
|
||||
BldCfgOptionGroupRaw optGrpRaw = {.name = optGrp.value};
|
||||
|
||||
yamlEventNextCheck(yaml, yamlEventTypeMapBegin);
|
||||
|
||||
YamlEvent optGrpDef = yamlEventNext(yaml);
|
||||
|
||||
do
|
||||
{
|
||||
yamlEventCheck(optGrpDef, yamlEventTypeScalar);
|
||||
YamlEvent optGrpDefVal = yamlEventNextCheck(yaml, yamlEventTypeScalar);
|
||||
|
||||
if (strEqZ(optGrpDef.value, "indexTotal"))
|
||||
{
|
||||
optGrpRaw.indexTotal = varNewUInt(cvtZToUInt(strZ(optGrpDefVal.value)));
|
||||
}
|
||||
else if (strEqZ(optGrpDef.value, "prefix"))
|
||||
{
|
||||
// ??? This is the same as the name so should be removed
|
||||
}
|
||||
else
|
||||
THROW_FMT(FormatError, "unknown option group definition '%s'", strZ(optGrpDef.value));
|
||||
|
||||
optGrpDef = yamlEventNext(yaml);
|
||||
}
|
||||
while (optGrpDef.type != yamlEventTypeMapEnd);
|
||||
|
||||
// indexTotal is required
|
||||
if (optGrpRaw.indexTotal == NULL)
|
||||
THROW_FMT(FormatError, "option group '%s' requires 'indexTotal'", strZ(optGrpRaw.name));
|
||||
yamlEventNextCheck(yaml, yamlEventTypeMapEnd);
|
||||
|
||||
MEM_CONTEXT_BEGIN(lstMemContext(result))
|
||||
{
|
||||
lstAdd(result, &(BldCfgOptionGroup){.name = strDup(optGrpRaw.name), .indexTotal = varUInt(optGrpRaw.indexTotal)});
|
||||
lstAdd(result, &(BldCfgOptionGroup){.name = strDup(optGrpRaw.name)});
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
@ -294,8 +268,8 @@ typedef struct BldCfgOptionDependRaw
|
||||
typedef struct BldCfgOptionDeprecateRaw
|
||||
{
|
||||
const String *name; // See BldCfgOptionDeprecate for comments
|
||||
unsigned int index;
|
||||
bool reset;
|
||||
bool indexed;
|
||||
bool unindexed;
|
||||
} BldCfgOptionDeprecateRaw;
|
||||
|
||||
typedef struct BldCfgOptionCommandRaw
|
||||
@ -533,50 +507,42 @@ bldCfgParseOptionDeprecate(Yaml *const yaml)
|
||||
do
|
||||
{
|
||||
yamlEventCheck(optDeprecate, yamlEventTypeScalar);
|
||||
BldCfgOptionDeprecateRaw optDeprecateRaw = {.name = optDeprecate.value, .reset = true};
|
||||
const String *name = optDeprecate.value;
|
||||
bool indexed = false;
|
||||
|
||||
yamlEventNextCheck(yaml, yamlEventTypeMapBegin);
|
||||
yamlEventNextCheck(yaml, yamlEventTypeMapEnd);
|
||||
|
||||
YamlEvent optDeprecateDef = yamlEventNext(yaml);
|
||||
// Determine if this deprecation is indexed
|
||||
const size_t questionPos = (size_t)strChr(name, '?');
|
||||
|
||||
if (optDeprecateDef.type == yamlEventTypeScalar)
|
||||
if (questionPos != (size_t)-1)
|
||||
{
|
||||
do
|
||||
{
|
||||
yamlEventCheck(optDeprecateDef, yamlEventTypeScalar);
|
||||
YamlEvent optDeprecateDefVal = yamlEventNextCheck(yaml, yamlEventTypeScalar);
|
||||
name = strNewFmt("%s%s", strZ(strSubN(name, 0, questionPos)), strZ(strSub(name, questionPos + 1)));
|
||||
indexed = true;
|
||||
}
|
||||
|
||||
if (strEqZ(optDeprecateDef.value, "index"))
|
||||
{
|
||||
optDeprecateRaw.index = cvtZToUInt(strZ(optDeprecateDefVal.value));
|
||||
}
|
||||
else if (strEqZ(optDeprecateDef.value, "reset"))
|
||||
{
|
||||
optDeprecateRaw.reset = yamlBoolParse(optDeprecateDefVal);
|
||||
}
|
||||
else
|
||||
THROW_FMT(FormatError, "unknown deprecate definition '%s'", strZ(optDeprecateDef.value));
|
||||
|
||||
optDeprecateDef = yamlEventNext(yaml);
|
||||
}
|
||||
while (optDeprecateDef.type != yamlEventTypeMapEnd);
|
||||
}
|
||||
else
|
||||
yamlEventCheck(optDeprecateDef, yamlEventTypeMapEnd);
|
||||
// Create final deprecation if it does not aready exist
|
||||
BldCfgOptionDeprecateRaw *deprecate = lstFind(result, &name);
|
||||
|
||||
if (deprecate == NULL)
|
||||
{
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
lstAdd(
|
||||
result,
|
||||
&(BldCfgOptionDeprecate)
|
||||
{
|
||||
.name = strDup(optDeprecateRaw.name),
|
||||
.index = optDeprecateRaw.index,
|
||||
.reset = optDeprecateRaw.reset,
|
||||
});
|
||||
lstAdd(result, &(BldCfgOptionDeprecateRaw){.name = strDup(name)});
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
|
||||
deprecate = lstFind(result, &name);
|
||||
CHECK(deprecate != NULL);
|
||||
}
|
||||
|
||||
// Set indexed/unindexed flags
|
||||
if (indexed)
|
||||
deprecate->indexed = true;
|
||||
else
|
||||
deprecate->unindexed = true;
|
||||
|
||||
optDeprecate = yamlEventNext(yaml);
|
||||
}
|
||||
while (optDeprecate.type != yamlEventTypeMapEnd);
|
||||
@ -607,8 +573,8 @@ bldCfgParseOptionDeprecateReconcile(const List *const optDeprecateRawList)
|
||||
&(BldCfgOptionDeprecate)
|
||||
{
|
||||
.name = strDup(optDeprecateRaw->name),
|
||||
.index = optDeprecateRaw->index,
|
||||
.reset = optDeprecateRaw->reset,
|
||||
.indexed = optDeprecateRaw->indexed,
|
||||
.unindexed = optDeprecateRaw->unindexed,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,6 @@ typedef struct BldCfgCommand
|
||||
typedef struct BldCfgOptionGroup
|
||||
{
|
||||
const String *const name; // Name
|
||||
const unsigned int indexTotal; // Total indexes for option
|
||||
} BldCfgOptionGroup;
|
||||
|
||||
typedef struct BldCfgOption BldCfgOption; // Forward declaration
|
||||
@ -88,8 +87,8 @@ typedef struct BldCfgOptionDepend
|
||||
typedef struct BldCfgOptionDeprecate
|
||||
{
|
||||
const String *const name; // Deprecated option name
|
||||
const unsigned int index; // Option index to deprecate
|
||||
const bool reset; // Does the deprecated option allow reset
|
||||
bool indexed; // Can the deprecation be indexed?
|
||||
bool unindexed; // Can the deprecation be unindexed?
|
||||
} BldCfgOptionDeprecate;
|
||||
|
||||
typedef struct BldCfgOptionCommand
|
||||
|
@ -281,6 +281,14 @@ Render parse.auto.c
|
||||
***********************************************************************************************************************************/
|
||||
#define PARSE_AUTO_COMMENT "Config Parse Rules"
|
||||
|
||||
typedef struct BldCfgRenderOptionDeprecate
|
||||
{
|
||||
const String *const name; // Deprecated option name
|
||||
const BldCfgOption *const option; // Option
|
||||
const bool indexed; // Can the deprecation be indexed?
|
||||
const bool unindexed; // Can the deprecation be unindexed?
|
||||
} BldCfgRenderOptionDeprecate;
|
||||
|
||||
static void
|
||||
bldCfgRenderLf(String *const config, const bool lf)
|
||||
{
|
||||
@ -536,11 +544,20 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
|
||||
" PARSE_RULE_OPTION\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"%s\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(%s),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(%s),\n",
|
||||
strZ(opt->name), strZ(bldEnum("cfgOptType", opt->type)));
|
||||
|
||||
if (opt->negate)
|
||||
strCatZ(config, " PARSE_RULE_OPTION_NEGATE(true),\n");
|
||||
|
||||
if (opt->reset)
|
||||
strCatZ(config, " PARSE_RULE_OPTION_RESET(true),\n");
|
||||
|
||||
strCatFmt(
|
||||
config,
|
||||
" PARSE_RULE_OPTION_REQUIRED(%s),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(%s),\n",
|
||||
strZ(opt->name), strZ(bldEnum("cfgOptType", opt->type)), cvtBoolToConstZ(opt->required),
|
||||
strZ(bldEnum("cfgSection", opt->section)));
|
||||
cvtBoolToConstZ(opt->required), strZ(bldEnum("cfgSection", opt->section)));
|
||||
|
||||
if (opt->secure)
|
||||
strCatZ(config, " PARSE_RULE_OPTION_SECURE(true),\n");
|
||||
@ -557,6 +574,21 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
|
||||
strZ(bldEnum("cfgOptGrp", opt->group)));
|
||||
}
|
||||
|
||||
// If an unindexed deprecation matches the base option name of an indexed option
|
||||
if (opt->deprecateList != NULL)
|
||||
{
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < lstSize(opt->deprecateList); deprecateIdx++)
|
||||
{
|
||||
const BldCfgOptionDeprecate *const deprecate = lstGet(opt->deprecateList, deprecateIdx);
|
||||
|
||||
if (strEq(deprecate->name, opt->name) && deprecate->unindexed)
|
||||
{
|
||||
strCatZ(config, " PARSE_RULE_OPTION_DEPRECATE_MATCH(true),\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build command role valid lists
|
||||
for (unsigned int cmdRoleAllIdx = 0; cmdRoleAllIdx < strLstSize(cmdRoleAllList); cmdRoleAllIdx++)
|
||||
{
|
||||
@ -671,153 +703,71 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg)
|
||||
config,
|
||||
"};\n");
|
||||
|
||||
// Option data for getopt_long()
|
||||
// Option deprecations
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
strCatFmt(
|
||||
config,
|
||||
"\n"
|
||||
COMMENT_BLOCK_BEGIN "\n"
|
||||
"Option data for getopt_long()\n"
|
||||
COMMENT_BLOCK_END "\n"
|
||||
"static const struct option optionList[] =\n"
|
||||
"{\n");
|
||||
List *const deprecateCombineList = lstNewP(sizeof(BldCfgRenderOptionDeprecate), .comparator = lstComparatorStr);
|
||||
|
||||
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
|
||||
{
|
||||
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
|
||||
|
||||
// Determine if the option is indexed
|
||||
unsigned int indexTotal = 1;
|
||||
const BldCfgOptionGroup *optGrp = NULL;
|
||||
|
||||
if (opt->group != NULL)
|
||||
{
|
||||
optGrp = lstFind(bldCfg.optGrpList, &opt->group);
|
||||
CHECK(optGrp != NULL);
|
||||
|
||||
indexTotal = optGrp->indexTotal;
|
||||
}
|
||||
|
||||
bldCfgRenderLf(config, optIdx != 0);
|
||||
|
||||
strCatFmt(
|
||||
config,
|
||||
" // %s option%s\n"
|
||||
COMMENT_SEPARATOR "\n",
|
||||
strZ(opt->name), opt->deprecateList != NULL ? " and deprecations" : "");
|
||||
|
||||
for (unsigned int index = 0; index < indexTotal; index++)
|
||||
{
|
||||
const String *optName = opt->name;
|
||||
const String *optShift = EMPTY_STR;
|
||||
|
||||
// Generate indexed name
|
||||
if (optGrp != NULL)
|
||||
{
|
||||
optName = strNewFmt("%s%u%s", strZ(optGrp->name), index + 1, strZ(strSub(optName, strSize(optGrp->name))));
|
||||
optShift = strNewFmt(" | (%u << PARSE_KEY_IDX_SHIFT)", index);
|
||||
}
|
||||
|
||||
strCatFmt(
|
||||
config,
|
||||
" {\n"
|
||||
" .name = \"%s\",\n"
|
||||
"%s"
|
||||
" .val = PARSE_OPTION_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(optName),
|
||||
strEq(opt->type, OPT_TYPE_BOOLEAN_STR) ? "" : " .has_arg = required_argument,\n", strZ(optShift),
|
||||
strZ(bldEnum("cfgOpt", opt->name)));
|
||||
|
||||
if (opt->negate)
|
||||
{
|
||||
strCatFmt(
|
||||
config,
|
||||
" {\n"
|
||||
" .name = \"no-%s\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_NEGATE_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(optName), strZ(optShift), strZ(bldEnum("cfgOpt", opt->name)));
|
||||
}
|
||||
|
||||
if (opt->reset)
|
||||
{
|
||||
strCatFmt(
|
||||
config,
|
||||
" {\n"
|
||||
" .name = \"reset-%s\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(optName), strZ(optShift), strZ(bldEnum("cfgOpt", opt->name)));
|
||||
}
|
||||
|
||||
if (opt->deprecateList != 0)
|
||||
if (opt->deprecateList != NULL)
|
||||
{
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < lstSize(opt->deprecateList); deprecateIdx++)
|
||||
{
|
||||
const BldCfgOptionDeprecate *const deprecate = lstGet(opt->deprecateList, deprecateIdx);
|
||||
const String *deprecateName = deprecate->name;
|
||||
|
||||
// Skip the deprecation if it does not apply to this index
|
||||
if (deprecate->index > 0 && deprecate->index != index + 1)
|
||||
continue;
|
||||
|
||||
// Generate name if deprecation applies to all indexes
|
||||
int deprecateIndexPos = strChr(deprecateName, '?');
|
||||
|
||||
if (deprecateIndexPos != -1)
|
||||
lstAdd(
|
||||
deprecateCombineList,
|
||||
&(BldCfgRenderOptionDeprecate)
|
||||
{
|
||||
CHECK(optGrp != NULL);
|
||||
|
||||
deprecateName = strNewFmt(
|
||||
"%s%u%s", strZ(strSubN(deprecateName, 0, (size_t)deprecateIndexPos)), index + 1,
|
||||
strZ(strSub(deprecateName, (size_t)deprecateIndexPos + 1)));
|
||||
.name = deprecate->name,
|
||||
.option = opt,
|
||||
.indexed = deprecate->indexed,
|
||||
.unindexed = deprecate->unindexed,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lstSort(deprecateCombineList, sortOrderAsc);
|
||||
|
||||
strCatFmt(
|
||||
config,
|
||||
"\n"
|
||||
COMMENT_BLOCK_BEGIN "\n"
|
||||
"Option deprecations\n"
|
||||
COMMENT_BLOCK_END "\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"static const ParseRuleOptionDeprecate parseRuleOptionDeprecate[CFG_OPTION_DEPRECATE_TOTAL] =\n"
|
||||
"{\n",
|
||||
strZ(bldDefineRender(STRDEF("CFG_OPTION_DEPRECATE_TOTAL"), strNewFmt("%u", lstSize(deprecateCombineList)))));
|
||||
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < lstSize(deprecateCombineList); deprecateIdx++)
|
||||
{
|
||||
const BldCfgRenderOptionDeprecate *const deprecate = lstGet(deprecateCombineList, deprecateIdx);
|
||||
|
||||
bldCfgRenderLf(config, deprecateIdx != 0);
|
||||
|
||||
strCatFmt(
|
||||
config,
|
||||
" // %s deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"%s\",\n"
|
||||
"%s"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(deprecateName),
|
||||
strEq(opt->type, OPT_TYPE_BOOLEAN_STR) ? "" : " .has_arg = required_argument,\n", strZ(optShift),
|
||||
strZ(bldEnum("cfgOpt", opt->name)));
|
||||
" .id = %s,\n",
|
||||
strZ(deprecate->option->name), strZ(deprecate->name), strZ(bldEnum("cfgOpt", deprecate->option->name)));
|
||||
|
||||
if (opt->negate)
|
||||
{
|
||||
strCatFmt(
|
||||
config,
|
||||
" {\n"
|
||||
" .name = \"no-%s\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | PARSE_NEGATE_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(deprecateName), strZ(optShift), strZ(bldEnum("cfgOpt", opt->name)));
|
||||
if (deprecate->indexed)
|
||||
strCatZ(config, " .indexed = true,\n");
|
||||
|
||||
if (deprecate->unindexed)
|
||||
strCatZ(config, " .unindexed = true,\n");
|
||||
|
||||
strCatZ(config, " },\n");
|
||||
}
|
||||
|
||||
if (opt->reset && deprecate->reset)
|
||||
{
|
||||
strCatFmt(
|
||||
config,
|
||||
" {\n"
|
||||
" .name = \"reset-%s\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | PARSE_RESET_FLAG%s | %s,\n"
|
||||
" },\n",
|
||||
strZ(deprecateName), strZ(optShift), strZ(bldEnum("cfgOpt", opt->name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strCatZ(
|
||||
config,
|
||||
" // Terminate option list\n"
|
||||
" {\n"
|
||||
" .name = NULL\n"
|
||||
" }\n"
|
||||
"};\n");
|
||||
strCatZ(config, "};\n");
|
||||
|
||||
// Order for option parse resolution
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -484,20 +484,7 @@ helpRender(void)
|
||||
|
||||
// Ensure the option is valid
|
||||
const String *optionName = strLstGet(cfgCommandParam(), 0);
|
||||
CfgParseOptionResult option = cfgParseOptionP(optionName);
|
||||
|
||||
// If the option was not found it might be an indexed option without the index, e.g. repo-host instead of
|
||||
// repo1-host. This is valid for help even though the parser will reject it.
|
||||
if (!option.found)
|
||||
{
|
||||
int optionId = cfgParseOptionId(strZ(optionName));
|
||||
|
||||
if (optionId != -1)
|
||||
{
|
||||
option.id = (unsigned int)optionId;
|
||||
option.found = true;
|
||||
}
|
||||
}
|
||||
CfgParseOptionResult option = cfgParseOptionP(optionName, .ignoreMissingIndex = true);
|
||||
|
||||
// Error when option is not found or is invalid for the current command
|
||||
if (!option.found || !cfgParseOptionValid(cfgCommand(), cfgCmdRoleMain, option.id))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,8 @@ Command and Option Parse
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
@ -12,6 +13,7 @@ Command and Option Parse
|
||||
#include "common/error.h"
|
||||
#include "common/ini.h"
|
||||
#include "common/log.h"
|
||||
#include "common/macro.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/regExp.h"
|
||||
#include "config/config.intern.h"
|
||||
@ -54,28 +56,6 @@ Option value constants
|
||||
VARIANT_STRDEF_STATIC(OPTION_VALUE_0, ZERO_Z);
|
||||
VARIANT_STRDEF_STATIC(OPTION_VALUE_1, ONE_Z);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse option flags
|
||||
***********************************************************************************************************************************/
|
||||
// Offset the option values so they don't conflict with getopt_long return codes
|
||||
#define PARSE_OPTION_FLAG (1 << 30)
|
||||
|
||||
// Add a flag for negation rather than checking "--no-"
|
||||
#define PARSE_NEGATE_FLAG (1 << 29)
|
||||
|
||||
// Add a flag for reset rather than checking "--reset-"
|
||||
#define PARSE_RESET_FLAG (1 << 28)
|
||||
|
||||
// Indicate that option name has been deprecated and will be removed in a future release
|
||||
#define PARSE_DEPRECATE_FLAG (1 << 27)
|
||||
|
||||
// Mask for option id (must be 0-255)
|
||||
#define PARSE_OPTION_MASK 0xFF
|
||||
|
||||
// Shift and mask for option key index (must be 0-255)
|
||||
#define PARSE_KEY_IDX_SHIFT 8
|
||||
#define PARSE_KEY_IDX_MASK 0xFF
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define how a command is parsed
|
||||
***********************************************************************************************************************************/
|
||||
@ -144,12 +124,15 @@ typedef struct ParseRuleOption
|
||||
{
|
||||
const char *name; // Name
|
||||
unsigned int type:3; // e.g. string, int, boolean
|
||||
bool negate:1; // Can the option be negated on the command line?
|
||||
bool reset:1; // Can the option be reset on the command line?
|
||||
bool required:1; // Is the option required?
|
||||
unsigned int section:2; // e.g. global, stanza, cmd-line
|
||||
bool secure:1; // Needs to be redacted in logs and cmd-line?
|
||||
bool multi:1; // Can be specified multiple times?
|
||||
bool group:1; // In a group?
|
||||
unsigned int groupId:1; // Id if in a group
|
||||
bool deprecateMatch:1; // Does a deprecated name exactly match the option name?
|
||||
uint32_t commandRoleValid[CFG_COMMAND_ROLE_TOTAL]; // Valid for the command role?
|
||||
|
||||
const void **data; // Optional data and command overrides
|
||||
@ -179,6 +162,12 @@ typedef enum
|
||||
#define PARSE_RULE_OPTION_TYPE(typeParam) \
|
||||
.type = typeParam
|
||||
|
||||
#define PARSE_RULE_OPTION_NEGATE(negateParam) \
|
||||
.negate = negateParam
|
||||
|
||||
#define PARSE_RULE_OPTION_RESET(resetParam) \
|
||||
.reset = resetParam
|
||||
|
||||
#define PARSE_RULE_OPTION_REQUIRED(requiredParam) \
|
||||
.required = requiredParam
|
||||
|
||||
@ -197,6 +186,9 @@ typedef enum
|
||||
#define PARSE_RULE_OPTION_GROUP_ID(groupIdParam) \
|
||||
.groupId = groupIdParam
|
||||
|
||||
#define PARSE_RULE_OPTION_DEPRECATE_MATCH(deprecateMatchParam) \
|
||||
.deprecateMatch = deprecateMatchParam
|
||||
|
||||
#define PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST(...) \
|
||||
.commandRoleValid[cfgCmdRoleMain] = 0 __VA_ARGS__
|
||||
|
||||
@ -252,6 +244,17 @@ typedef enum
|
||||
#define PARSE_RULE_OPTION_OPTIONAL_REQUIRED(requiredParam) \
|
||||
PARSE_RULE_OPTION_OPTIONAL_PUSH(parseRuleOptionDataTypeRequired, 0, requiredParam)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define option deprecations
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct ParseRuleOptionDeprecate
|
||||
{
|
||||
const char *name; // Deprecated name
|
||||
ConfigOption id; // Option Id
|
||||
bool indexed; // Can the deprecation be indexed?
|
||||
bool unindexed; // Can the deprecation be unindexed?
|
||||
} ParseRuleOptionDeprecate;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include automatically generated parse data
|
||||
***********************************************************************************************************************************/
|
||||
@ -523,55 +526,117 @@ cfgParseCommandRoleName(const ConfigCommand commandId, const ConfigCommandRole c
|
||||
/***********************************************************************************************************************************
|
||||
Find an option by name in the option list
|
||||
***********************************************************************************************************************************/
|
||||
// Helper to parse the option info into a structure
|
||||
__attribute__((always_inline)) static inline CfgParseOptionResult
|
||||
cfgParseOptionInfo(const int info)
|
||||
{
|
||||
return (CfgParseOptionResult)
|
||||
{
|
||||
.found = true,
|
||||
.id = info & PARSE_OPTION_MASK,
|
||||
.keyIdx = (info >> PARSE_KEY_IDX_SHIFT) & PARSE_KEY_IDX_MASK,
|
||||
.negate = info & PARSE_NEGATE_FLAG,
|
||||
.reset = info & PARSE_RESET_FLAG,
|
||||
.deprecated = info & PARSE_DEPRECATE_FLAG,
|
||||
};
|
||||
}
|
||||
#define OPTION_PREFIX_NEGATE "no-"
|
||||
#define OPTION_PREFIX_RESET "reset-"
|
||||
#define OPTION_NAME_SIZE_MAX 64
|
||||
|
||||
CfgParseOptionResult
|
||||
cfgParseOption(const String *const optionName, const CfgParseOptionParam param)
|
||||
cfgParseOption(const String *const optionCandidate, const CfgParseOptionParam param)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STRING, optionName);
|
||||
FUNCTION_TEST_PARAM(STRING, optionCandidate);
|
||||
FUNCTION_TEST_PARAM(BOOL, param.prefixMatch);
|
||||
FUNCTION_TEST_PARAM(BOOL, param.ignoreMissingIndex);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(optionName != NULL);
|
||||
ASSERT(optionCandidate != NULL);
|
||||
|
||||
// Search for an exact match
|
||||
unsigned int findIdx = 0;
|
||||
CfgParseOptionResult result = {0};
|
||||
|
||||
while (optionList[findIdx].name != NULL)
|
||||
// Copy the option to a buffer so it can be efficiently manipulated
|
||||
char optionName[OPTION_NAME_SIZE_MAX + 1];
|
||||
size_t optionNameSize = strSize(optionCandidate);
|
||||
|
||||
if (optionNameSize > sizeof(optionName) - 1)
|
||||
{
|
||||
if (strEqZ(optionName, optionList[findIdx].name))
|
||||
break;
|
||||
|
||||
findIdx++;
|
||||
THROW_FMT(
|
||||
OptionInvalidError, "option '%s' exceeds maximum size of " STRINGIFY(OPTION_NAME_SIZE_MAX), strZ(optionCandidate));
|
||||
}
|
||||
|
||||
// If the option was found
|
||||
if (optionList[findIdx].name != NULL)
|
||||
FUNCTION_TEST_RETURN(cfgParseOptionInfo(optionList[findIdx].val));
|
||||
strcpy(optionName, strZ(optionCandidate));
|
||||
|
||||
// Search for a single partial match if requested
|
||||
// If this looks like negate
|
||||
if (strncmp(optionName, OPTION_PREFIX_NEGATE, sizeof(OPTION_PREFIX_NEGATE) - 1) == 0)
|
||||
{
|
||||
result.negate = true;
|
||||
|
||||
// Strip the negate prefix
|
||||
optionNameSize -= sizeof(OPTION_PREFIX_NEGATE) - 1;
|
||||
memmove(optionName, optionName + (sizeof(OPTION_PREFIX_NEGATE) - 1), optionNameSize + 1);
|
||||
}
|
||||
// Else if looks like reset
|
||||
else if (strncmp(optionName, OPTION_PREFIX_RESET, sizeof(OPTION_PREFIX_RESET) - 1) == 0)
|
||||
{
|
||||
result.reset = true;
|
||||
|
||||
// Strip the reset prefix
|
||||
optionNameSize -= sizeof(OPTION_PREFIX_RESET) - 1;
|
||||
memmove(optionName, optionName + (sizeof(OPTION_PREFIX_RESET) - 1), optionNameSize + 1);
|
||||
}
|
||||
|
||||
// Indexed options must have at least one dash
|
||||
char *const dashPtr = strchr(optionName, '-');
|
||||
bool indexed = false;
|
||||
|
||||
if (dashPtr != NULL)
|
||||
{
|
||||
if (dashPtr == optionName)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' cannot begin with a dash", strZ(optionCandidate));
|
||||
|
||||
// Check if the first dash is preceeded by a numeric key and keep a tally of the key
|
||||
char *numberPtr = dashPtr;
|
||||
unsigned int multiplier = 1;
|
||||
|
||||
while (numberPtr > optionName && isdigit(*(numberPtr - 1)))
|
||||
{
|
||||
numberPtr--;
|
||||
|
||||
result.keyIdx += (unsigned int)(*numberPtr - '0') * multiplier;
|
||||
multiplier *= 10;
|
||||
}
|
||||
|
||||
if (numberPtr == optionName)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' cannot begin with a number", strZ(optionCandidate));
|
||||
|
||||
// If there was a number then the option is indexed
|
||||
if (numberPtr != dashPtr)
|
||||
{
|
||||
indexed = true;
|
||||
|
||||
// Strip the key to get the base option name
|
||||
optionNameSize -= (size_t)(dashPtr - numberPtr);
|
||||
memmove(numberPtr, dashPtr, optionNameSize + 1);
|
||||
|
||||
// Check that the index does not exceed the maximum
|
||||
if (result.keyIdx > CFG_OPTION_KEY_MAX)
|
||||
{
|
||||
THROW_FMT(
|
||||
OptionInvalidError, "option '%s' key exceeds maximum of " STRINGIFY(CFG_OPTION_KEY_MAX), strZ(optionCandidate));
|
||||
}
|
||||
|
||||
// Subtract one to represent a key index
|
||||
result.keyIdx--;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for an exact match. A copy of the option name must be made because bsearch() requires a reference.
|
||||
const char *const optionNamePtr = optionName;
|
||||
|
||||
const ParseRuleOption *optionFound = bsearch(
|
||||
&optionNamePtr, parseRuleOption, CFG_OPTION_TOTAL, sizeof(ParseRuleOption), lstComparatorZ);
|
||||
|
||||
// If the option was not found
|
||||
if (optionFound == NULL)
|
||||
{
|
||||
// Search for a single partial match (if requested)
|
||||
if (param.prefixMatch)
|
||||
{
|
||||
unsigned int findPartialIdx = 0;
|
||||
unsigned int findPartialTotal = 0;
|
||||
|
||||
for (findIdx = 0; findIdx < sizeof(optionList) / sizeof(struct option) - 1; findIdx++)
|
||||
for (unsigned int findIdx = 0; findIdx < CFG_OPTION_TOTAL; findIdx++)
|
||||
{
|
||||
if (strBeginsWith(STR(optionList[findIdx].name), optionName))
|
||||
if (strncmp(parseRuleOption[findIdx].name, optionName, optionNameSize) == 0)
|
||||
{
|
||||
findPartialIdx = findIdx;
|
||||
findPartialTotal++;
|
||||
@ -583,7 +648,91 @@ cfgParseOption(const String *const optionName, const CfgParseOptionParam param)
|
||||
|
||||
// If a single partial match was found
|
||||
if (findPartialTotal == 1)
|
||||
FUNCTION_TEST_RETURN(cfgParseOptionInfo(optionList[findPartialIdx].val));
|
||||
optionFound = &parseRuleOption[findPartialIdx];
|
||||
}
|
||||
|
||||
// If the option was not found search deprecations
|
||||
if (optionFound == NULL)
|
||||
{
|
||||
// Search deprecations for an exact match
|
||||
const ParseRuleOptionDeprecate *deprecate = bsearch(
|
||||
&optionNamePtr, parseRuleOptionDeprecate, CFG_OPTION_DEPRECATE_TOTAL, sizeof(ParseRuleOptionDeprecate),
|
||||
lstComparatorZ);
|
||||
|
||||
// If the option was not found then search deprecations for a single partial match (if requested)
|
||||
if (deprecate == NULL && param.prefixMatch)
|
||||
{
|
||||
unsigned int findPartialIdx = 0;
|
||||
unsigned int findPartialTotal = 0;
|
||||
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < CFG_OPTION_DEPRECATE_TOTAL; deprecateIdx++)
|
||||
{
|
||||
if (strncmp(parseRuleOptionDeprecate[deprecateIdx].name, optionName, optionNameSize) == 0)
|
||||
{
|
||||
findPartialIdx = deprecateIdx;
|
||||
findPartialTotal++;
|
||||
|
||||
if (findPartialTotal > 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If a single partial match was found
|
||||
if (findPartialTotal == 1)
|
||||
deprecate = &parseRuleOptionDeprecate[findPartialIdx];
|
||||
}
|
||||
|
||||
// Deprecation was found
|
||||
if (deprecate != NULL)
|
||||
{
|
||||
// Error if the option is indexed but the deprecation is not
|
||||
if (indexed && !deprecate->indexed)
|
||||
THROW_FMT(OptionInvalidError, "deprecated option '%s' cannot have an index", strZ(optionCandidate));
|
||||
|
||||
// Error if the option is unindexed but the deprecation is not
|
||||
if (!indexed && !deprecate->unindexed && !param.ignoreMissingIndex)
|
||||
THROW_FMT(OptionInvalidError, "deprecated option '%s' must have an index", strZ(optionCandidate));
|
||||
|
||||
result.deprecated = true;
|
||||
optionFound = &parseRuleOption[deprecate->id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Option was found
|
||||
if (optionFound != NULL)
|
||||
{
|
||||
result.found = true;
|
||||
result.id = (unsigned int)(optionFound - parseRuleOption);
|
||||
|
||||
// Error if negate is not allowed
|
||||
if (result.negate && !optionFound->negate)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' cannot be negated", strZ(optionCandidate));
|
||||
|
||||
// Error if reset is not allowed
|
||||
if (result.reset && !optionFound->reset)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' cannot be reset", strZ(optionCandidate));
|
||||
|
||||
// It is possible for an unindexed deprecation to match an indexed option name (without the index) exactly. For example, the
|
||||
// deprecated repo-path option now maps to repo-path index 0, which will yield an exact match. In this case we still need to
|
||||
// mark the option as deprecated.
|
||||
if (indexed == false && optionFound->deprecateMatch)
|
||||
result.deprecated = true;
|
||||
|
||||
// If not deprecated make sure indexing matches. Deprecation indexing has already been checked because the rules are per
|
||||
// deprecation.
|
||||
if (!result.deprecated)
|
||||
{
|
||||
// Error if the option is indexed but should not be
|
||||
if (indexed && !optionFound->group)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' cannot have an index", strZ(optionCandidate));
|
||||
|
||||
// Error if the option is unindexed but the deprecation is not
|
||||
if (!indexed && optionFound->group && !param.ignoreMissingIndex)
|
||||
THROW_FMT(OptionInvalidError, "option '%s' must have an index", strZ(optionCandidate));
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN((CfgParseOptionResult){0});
|
||||
@ -609,25 +758,6 @@ cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId)
|
||||
FUNCTION_TEST_RETURN(NULL);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
int
|
||||
cfgParseOptionId(const char *optionName)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STRINGZ, optionName);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(optionName != NULL);
|
||||
|
||||
int result = -1;
|
||||
|
||||
for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
|
||||
if (strcmp(optionName, parseRuleOption[optionId].name) == 0)
|
||||
result = (int)optionId;
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
const char *
|
||||
cfgParseOptionName(ConfigOption optionId)
|
||||
|
@ -43,6 +43,7 @@ typedef struct CfgParseOptionParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
bool prefixMatch; // Allow prefix matches, e.g. 'stanz' for 'stanza'
|
||||
bool ignoreMissingIndex; // Help requires the base option name to be searchable
|
||||
} CfgParseOptionParam;
|
||||
|
||||
typedef struct CfgParseOptionResult
|
||||
@ -63,9 +64,6 @@ CfgParseOptionResult cfgParseOption(const String *const optionName, const CfgPar
|
||||
// Default value for the option
|
||||
const char *cfgParseOptionDefault(ConfigCommand commandId, ConfigOption optionId);
|
||||
|
||||
// Option id from name
|
||||
int cfgParseOptionId(const char *optionName);
|
||||
|
||||
// Option name from id
|
||||
const char *cfgParseOptionName(ConfigOption optionId);
|
||||
|
||||
|
@ -99,12 +99,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
buffer-size=[BUFFER-SIZE]
|
||||
@ -210,12 +210,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
buffer-size=[BUFFER-SIZE]
|
||||
@ -321,12 +321,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
buffer-size=[BUFFER-SIZE]
|
||||
@ -366,7 +366,7 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.con
|
||||
standby_mode = 'on'
|
||||
|
||||
full backup - backup from standby, failure to access at least one standby (backup host)
|
||||
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --pg8-host=bogus --backup-standby --type=full --stanza=db backup
|
||||
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --pg256-host=bogus --backup-standby --type=full --stanza=db backup
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
full backup - backup from standby (backup host)
|
||||
@ -453,12 +453,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
archive-async=y
|
||||
@ -571,12 +571,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
archive-async=y
|
||||
@ -705,12 +705,12 @@ pg2-host-cmd=[BACKREST-BIN]
|
||||
pg2-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg2-host-user=[USER-1]
|
||||
pg2-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-host=db-standby
|
||||
pg8-host-cmd=[BACKREST-BIN]
|
||||
pg8-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg8-host-user=[USER-1]
|
||||
pg8-path=[TEST_PATH]/db-standby/db/base
|
||||
pg8-port=6544
|
||||
pg256-host=db-standby
|
||||
pg256-host-cmd=[BACKREST-BIN]
|
||||
pg256-host-config=[TEST_PATH]/db-standby/pgbackrest.conf
|
||||
pg256-host-user=[USER-1]
|
||||
pg256-path=[TEST_PATH]/db-standby/db/base
|
||||
pg256-port=6544
|
||||
|
||||
[global]
|
||||
archive-async=y
|
||||
|
@ -1308,16 +1308,16 @@ sub configCreate
|
||||
$self->{bBogusHost} = true;
|
||||
|
||||
# Set a valid replica to a higher index to ensure skipping indexes does not make a difference
|
||||
$oParamHash{$strStanza}{"pg8-host"} = $oHostDb2->nameGet();
|
||||
$oParamHash{$strStanza}{"pg8-host-user"} = $oHostDb2->userGet();
|
||||
$oParamHash{$strStanza}{"pg8-host-cmd"} = $oHostDb2->backrestExe();
|
||||
$oParamHash{$strStanza}{"pg8-host-config"} = $oHostDb2->backrestConfig();
|
||||
$oParamHash{$strStanza}{"pg8-path"} = $oHostDb2->dbBasePath();
|
||||
$oParamHash{$strStanza}{"pg256-host"} = $oHostDb2->nameGet();
|
||||
$oParamHash{$strStanza}{"pg256-host-user"} = $oHostDb2->userGet();
|
||||
$oParamHash{$strStanza}{"pg256-host-cmd"} = $oHostDb2->backrestExe();
|
||||
$oParamHash{$strStanza}{"pg256-host-config"} = $oHostDb2->backrestConfig();
|
||||
$oParamHash{$strStanza}{"pg256-path"} = $oHostDb2->dbBasePath();
|
||||
|
||||
# Only test explicit ports on the backup server. This is so locally configured ports are also tested.
|
||||
if (!$self->synthetic() && $self->nameTest(HOST_BACKUP))
|
||||
{
|
||||
$oParamHash{$strStanza}{"pg8-port"} = $oHostDb2->pgPort();
|
||||
$oParamHash{$strStanza}{"pg256-port"} = $oHostDb2->pgPort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,19 +284,19 @@ sub run
|
||||
|
||||
if ($oHostDbStandby->pgVersion() >= PG_VERSION_BACKUP_STANDBY)
|
||||
{
|
||||
# If there is only a primary and a replica and the replica is the backup destination, then if pg2-host and pg8-host
|
||||
# are BOGUS, confirm failure to reach the primary
|
||||
# If there is only a primary and a replica and the replica is the backup destination, then if pg2-host and
|
||||
# pg256-host are BOGUS, confirm failure to reach the primary
|
||||
if (!$bHostBackup && $bHostStandby && $strBackupDestination eq HOST_DB_STANDBY)
|
||||
{
|
||||
my $strStandbyBackup = $oHostBackup->backup(
|
||||
CFGOPTVAL_BACKUP_TYPE_FULL, 'backup from standby, failure to reach primary',
|
||||
{bStandby => true, iExpectedExitStatus => ERROR_DB_CONNECT, strOptionalParam => '--pg8-host=' . BOGUS});
|
||||
{bStandby => true, iExpectedExitStatus => ERROR_DB_CONNECT, strOptionalParam => '--pg256-host=' . BOGUS});
|
||||
}
|
||||
else
|
||||
{
|
||||
my $strStandbyBackup = $oHostBackup->backup(
|
||||
CFGOPTVAL_BACKUP_TYPE_FULL, 'backup from standby, failure to access at least one standby',
|
||||
{bStandby => true, iExpectedExitStatus => ERROR_DB_CONNECT, strOptionalParam => '--pg8-host=' . BOGUS});
|
||||
{bStandby => true, iExpectedExitStatus => ERROR_DB_CONNECT, strOptionalParam => '--pg256-host=' . BOGUS});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,31 +34,9 @@ testRun(void)
|
||||
" internal: true\n" \
|
||||
"\n"
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("option group parse errors");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
TEST_COMMAND_VALID
|
||||
"optionGroup:\n"
|
||||
" repo:\n"
|
||||
" bogus: test\n");
|
||||
|
||||
TEST_ERROR(bldCfgParse(storageTest), FormatError, "unknown option group definition 'bogus'");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
TEST_COMMAND_VALID
|
||||
"optionGroup:\n"
|
||||
" repo: \n"
|
||||
" prefix: test\n");
|
||||
|
||||
TEST_ERROR(bldCfgParse(storageTest), FormatError, "option group 'repo' requires 'indexTotal'");
|
||||
|
||||
#define TEST_OPTION_GROUP_VALID \
|
||||
"optionGroup:\n" \
|
||||
" repo:\n" \
|
||||
" indexTotal: 2\n" \
|
||||
" repo: {}\n" \
|
||||
"\n"
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -95,17 +73,6 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(bldCfgParse(storageTest), FormatError, "unknown depend definition 'bogus'");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
TEST_COMMAND_VALID
|
||||
TEST_OPTION_GROUP_VALID
|
||||
"option:\n"
|
||||
" config:\n"
|
||||
" deprecate:\n"
|
||||
" old: {bogus: test}\n");
|
||||
|
||||
TEST_ERROR(bldCfgParse(storageTest), FormatError, "unknown deprecate definition 'bogus'");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
TEST_COMMAND_VALID
|
||||
@ -254,12 +221,8 @@ testRun(void)
|
||||
" version: {}\n"
|
||||
"\n"
|
||||
"optionGroup:\n"
|
||||
" pg:\n"
|
||||
" indexTotal: 2\n"
|
||||
" prefix: pg\n"
|
||||
" repo:\n"
|
||||
" indexTotal: 2\n"
|
||||
" prefix: repo\n"
|
||||
" pg: {}\n"
|
||||
" repo: {}\n"
|
||||
"\n"
|
||||
"option:\n"
|
||||
" timeout:\n"
|
||||
@ -373,8 +336,17 @@ testRun(void)
|
||||
" group: pg\n"
|
||||
" type: hash\n"
|
||||
" deprecate:\n"
|
||||
" db-path: {index: 1, reset: false}\n"
|
||||
" db?-path: {reset: false}\n");
|
||||
" pg-path: {}\n"
|
||||
" pg?-path-indexed: {}\n"
|
||||
" db-path: {}\n"
|
||||
" db?-path: {}\n"
|
||||
"\n"
|
||||
" pg-host:\n"
|
||||
" section: stanza\n"
|
||||
" group: pg\n"
|
||||
" type: string\n"
|
||||
" deprecate:\n"
|
||||
" pg?-host: {}\n");
|
||||
|
||||
TEST_RESULT_VOID(bldCfgRender(storageTest, bldCfgParse(storageTest)), "parse and render");
|
||||
|
||||
@ -421,7 +393,7 @@ testRun(void)
|
||||
"#define CFGOPT_STANZA \"stanza\"\n"
|
||||
"#define CFGOPT_TIMEOUT \"timeout\"\n"
|
||||
"\n"
|
||||
"#define CFG_OPTION_TOTAL 11\n"
|
||||
"#define CFG_OPTION_TOTAL 12\n"
|
||||
"\n"
|
||||
COMMENT_BLOCK_BEGIN "\n"
|
||||
"Option value constants\n"
|
||||
@ -478,6 +450,7 @@ testRun(void)
|
||||
" cfgOptLogLevelConsole,\n"
|
||||
" cfgOptLogLevelFile,\n"
|
||||
" cfgOptOnline,\n"
|
||||
" cfgOptPgHost,\n"
|
||||
" cfgOptPgPath,\n"
|
||||
" cfgOptStanza,\n"
|
||||
" cfgOptTimeout,\n"
|
||||
@ -594,6 +567,8 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"backup-standby\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeBoolean),\n"
|
||||
" PARSE_RULE_OPTION_NEGATE(true),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
"\n"
|
||||
@ -614,6 +589,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"compress-level\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeInteger),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(false),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
"\n"
|
||||
@ -658,6 +634,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"compress-level-network\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeInteger),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(false),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
" PARSE_RULE_OPTION_SECURE(true),\n"
|
||||
@ -709,6 +686,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"compress-type\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeString),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
"\n"
|
||||
@ -730,6 +708,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"config\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeString),\n"
|
||||
" PARSE_RULE_OPTION_NEGATE(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionCommandLine),\n"
|
||||
"\n"
|
||||
@ -767,6 +746,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"log-level-console\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeString),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
"\n"
|
||||
@ -812,6 +792,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"log-level-file\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeString),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionGlobal),\n"
|
||||
"\n"
|
||||
@ -893,13 +874,50 @@ testRun(void)
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" PARSE_RULE_OPTION\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"pg-host\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeString),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionStanza),\n"
|
||||
" PARSE_RULE_OPTION_GROUP_MEMBER(true),\n"
|
||||
" PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpPg),\n"
|
||||
"\n"
|
||||
" PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
|
||||
" ),\n"
|
||||
"\n"
|
||||
" PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
|
||||
" ),\n"
|
||||
"\n"
|
||||
" PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
|
||||
" ),\n"
|
||||
"\n"
|
||||
" PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet)\n"
|
||||
" PARSE_RULE_OPTION_COMMAND(cfgCmdBackup)\n"
|
||||
" ),\n"
|
||||
" ),\n"
|
||||
"\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" PARSE_RULE_OPTION\n"
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"pg-path\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeHash),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
" PARSE_RULE_OPTION_SECTION(cfgSectionStanza),\n"
|
||||
" PARSE_RULE_OPTION_MULTI(true),\n"
|
||||
" PARSE_RULE_OPTION_GROUP_MEMBER(true),\n"
|
||||
" PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpPg),\n"
|
||||
" PARSE_RULE_OPTION_DEPRECATE_MATCH(true),\n"
|
||||
"\n"
|
||||
" PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST\n"
|
||||
" (\n"
|
||||
@ -998,184 +1016,61 @@ testRun(void)
|
||||
"};\n"
|
||||
"\n"
|
||||
COMMENT_BLOCK_BEGIN "\n"
|
||||
"Option data for getopt_long()\n"
|
||||
"Option deprecations\n"
|
||||
COMMENT_BLOCK_END "\n"
|
||||
"static const struct option optionList[] =\n"
|
||||
"#define CFG_OPTION_DEPRECATE_TOTAL 7\n"
|
||||
"\n"
|
||||
"static const ParseRuleOptionDeprecate parseRuleOptionDeprecate[CFG_OPTION_DEPRECATE_TOTAL] =\n"
|
||||
"{\n"
|
||||
" // backup-standby option and deprecations\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"backup-standby\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptBackupStandby,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"no-backup-standby\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_NEGATE_FLAG | cfgOptBackupStandby,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-backup-standby\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptBackupStandby,\n"
|
||||
" },\n"
|
||||
" // backup-standby deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"backup-standby-old\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | cfgOptBackupStandby,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"no-backup-standby-old\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | PARSE_NEGATE_FLAG | cfgOptBackupStandby,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-backup-standby-old\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | PARSE_RESET_FLAG | cfgOptBackupStandby,\n"
|
||||
" .id = cfgOptBackupStandby,\n"
|
||||
" .unindexed = true,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // compress-level option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"compress-level\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptCompressLevel,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-compress-level\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptCompressLevel,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // compress-level-network option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"compress-level-network\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptCompressLevelNetwork,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-compress-level-network\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptCompressLevelNetwork,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // compress-type option and deprecations\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"compress-type\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptCompressType,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-compress-type\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptCompressType,\n"
|
||||
" },\n"
|
||||
" // compress-type deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"compress\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | cfgOptCompressType,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-compress\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | PARSE_RESET_FLAG | cfgOptCompressType,\n"
|
||||
" .id = cfgOptCompressType,\n"
|
||||
" .unindexed = true,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // config option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"config\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptConfig,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"no-config\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_NEGATE_FLAG | cfgOptConfig,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // log-level-console option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"log-level-console\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptLogLevelConsole,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-log-level-console\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptLogLevelConsole,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // log-level-file option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"log-level-file\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptLogLevelFile,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-log-level-file\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | cfgOptLogLevelFile,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // online option and deprecations\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"online\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptOnline,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"online-old\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | cfgOptOnline,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // pg-path option and deprecations\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" {\n"
|
||||
" .name = \"pg1-path\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | (0 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-pg1-path\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | (0 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" // pg-path deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"db-path\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | (0 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"db1-path\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | (0 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"pg2-path\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | (1 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"reset-pg2-path\",\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_RESET_FLAG | (1 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" .name = \"db2-path\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | PARSE_DEPRECATE_FLAG | (1 << PARSE_KEY_IDX_SHIFT) | cfgOptPgPath,\n"
|
||||
" .id = cfgOptPgPath,\n"
|
||||
" .indexed = true,\n"
|
||||
" .unindexed = true,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // stanza option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" // online deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"stanza\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptStanza,\n"
|
||||
" .name = \"online-old\",\n"
|
||||
" .id = cfgOptOnline,\n"
|
||||
" .unindexed = true,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // timeout option\n"
|
||||
COMMENT_SEPARATOR "\n"
|
||||
" // pg-host deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"timeout\",\n"
|
||||
" .has_arg = required_argument,\n"
|
||||
" .val = PARSE_OPTION_FLAG | cfgOptTimeout,\n"
|
||||
" .name = \"pg-host\",\n"
|
||||
" .id = cfgOptPgHost,\n"
|
||||
" .indexed = true,\n"
|
||||
" },\n"
|
||||
" // Terminate option list\n"
|
||||
"\n"
|
||||
" // pg-path deprecation\n"
|
||||
" {\n"
|
||||
" .name = NULL\n"
|
||||
" }\n"
|
||||
" .name = \"pg-path\",\n"
|
||||
" .id = cfgOptPgPath,\n"
|
||||
" .unindexed = true,\n"
|
||||
" },\n"
|
||||
"\n"
|
||||
" // pg-path deprecation\n"
|
||||
" {\n"
|
||||
" .name = \"pg-path-indexed\",\n"
|
||||
" .id = cfgOptPgPath,\n"
|
||||
" .indexed = true,\n"
|
||||
" },\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
COMMENT_BLOCK_BEGIN "\n"
|
||||
@ -1188,6 +1083,7 @@ testRun(void)
|
||||
" cfgOptLogLevelConsole,\n"
|
||||
" cfgOptLogLevelFile,\n"
|
||||
" cfgOptOnline,\n"
|
||||
" cfgOptPgHost,\n"
|
||||
" cfgOptPgPath,\n"
|
||||
" cfgOptTimeout,\n"
|
||||
" cfgOptBackupStandby,\n"
|
||||
|
@ -48,7 +48,7 @@ testRun(void)
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, "/repo1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, "/repo4");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 64, "/repo64");
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "test");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/pg1");
|
||||
TEST_ERROR(
|
||||
@ -86,14 +86,14 @@ testRun(void)
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "test");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "3");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 4, "/repo4");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 3, "/repo4");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 3, "/repo3");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 33, "/repo3");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/pg1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoHost, 2, "host2");
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdRestore, argList), OptionInvalidValueError,
|
||||
"local repo3 and repo4 paths are both '/repo4' but must be different");
|
||||
"local repo3 and repo33 paths are both '/repo3' but must be different");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("repo can be specified for backup");
|
||||
@ -160,13 +160,13 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "test");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, "/pg1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 1, "pg1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 2, "/pg2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 2, "pg2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHostCmd, 2, "pg2-exe");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 99, "/pg99");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 99, "pg99");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHostCmd, 99, "pg99-exe");
|
||||
HRN_CFG_LOAD(cfgCmdCheck, argList);
|
||||
|
||||
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgHostCmd, 0), testProjectExe(), "check pg1-host-cmd");
|
||||
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgHostCmd, 1), "pg2-exe", "check pg2-host-cmd");
|
||||
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgHostCmd, 1), "pg99-exe", "check pg99-host-cmd");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("db-timeout set but not protocol timeout");
|
||||
@ -393,24 +393,24 @@ testRun(void)
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 2, "s3");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Bucket, 2, "bogus.bucket");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Region, 2, "region");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Endpoint, 2, "endpoint");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 2, "/repo");
|
||||
hrnCfgEnvKeyRawZ(cfgOptRepoS3Key, 2, "mykey");
|
||||
hrnCfgEnvKeyRawZ(cfgOptRepoS3KeySecret, 2, "mysecretkey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 111, "s3");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Bucket, 111, "bogus.bucket");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Region, 111, "region");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoS3Endpoint, 111, "endpoint");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 111, "/repo");
|
||||
hrnCfgEnvKeyRawZ(cfgOptRepoS3Key, 111, "mykey");
|
||||
hrnCfgEnvKeyRawZ(cfgOptRepoS3KeySecret, 111, "mysecretkey");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "111");
|
||||
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdArchiveGet, argList), OptionInvalidValueError,
|
||||
"'bogus.bucket' is not valid for option 'repo2-s3-bucket'"
|
||||
"'bogus.bucket' is not valid for option 'repo111-s3-bucket'"
|
||||
"\nHINT: RFC-2818 forbids dots in wildcard matches."
|
||||
"\nHINT: TLS/SSL verification cannot proceed with this bucket name."
|
||||
"\nHINT: remove dots from the bucket name.");
|
||||
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoS3Key, 2);
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoS3KeySecret, 2);
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoS3Key, 111);
|
||||
hrnCfgEnvKeyRemoveRaw(cfgOptRepoS3KeySecret, 111);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("invalid bucket name with verification disabled succeeds");
|
||||
|
@ -49,6 +49,7 @@ testRun(void)
|
||||
TEST_TITLE("check size of parse structures");
|
||||
|
||||
TEST_RESULT_UINT(sizeof(ParseRuleOption), TEST_64BIT() ? 40 : 28, "ParseRuleOption size");
|
||||
TEST_RESULT_UINT(sizeof(ParseRuleOptionDeprecate), TEST_64BIT() ? 16 : 12, "ParseRuleOptionDeprecate size");
|
||||
}
|
||||
|
||||
// Config functions that are not tested with parse
|
||||
@ -59,12 +60,6 @@ testRun(void)
|
||||
TEST_TITLE("config command defaults to none before cfgInit()");
|
||||
|
||||
TEST_RESULT_UINT(cfgCommand(), cfgCmdNone, "command is none");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("parse option name to id");
|
||||
|
||||
TEST_RESULT_INT(cfgParseOptionId(BOGUS_STR), -1, "invalid option");
|
||||
TEST_RESULT_INT(cfgParseOptionId(CFGOPT_STANZA), cfgOptStanza, "valid option");
|
||||
}
|
||||
|
||||
// config and config-include-path options
|
||||
@ -649,6 +644,16 @@ testRun(void)
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError, "invalid option '--c'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on ambiguous deprecated partial option");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--retention");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"invalid option '--retention'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on spaces in option name");
|
||||
|
||||
@ -677,8 +682,111 @@ testRun(void)
|
||||
"invalid option '--BOGUS'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("missing option argument");
|
||||
TEST_TITLE("error if option exceeds maximum size");
|
||||
|
||||
char optionMax[OPTION_NAME_SIZE_MAX + 2];
|
||||
|
||||
for (unsigned int optionMaxIdx = 0; optionMaxIdx < OPTION_NAME_SIZE_MAX + 1; optionMaxIdx++)
|
||||
optionMax[optionMaxIdx] = 'X';
|
||||
|
||||
optionMax[OPTION_NAME_SIZE_MAX + 1] = '\0';
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAdd(argList, strNewFmt("--%s", optionMax));
|
||||
TEST_ERROR_FMT(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option '%s' exceeds maximum size of 64", optionMax);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if option cannot be negated or reset");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--no-force");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option 'no-force' cannot be negated");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--reset-force");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option 'reset-force' cannot be reset");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if option exceeds key max");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--pg257-path");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option 'pg257-path' key exceeds maximum of 256");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if option begins with a number");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--257-path");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option '257-path' cannot begin with a number");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if option begins with a dash");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "---pg1-path");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option '-pg1-path' cannot begin with a dash");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if unindexed option cannot have an index");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--compress77-type");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option 'compress77-type' cannot have an index");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--retention128-full");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"deprecated option 'retention128-full' cannot have an index");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error if indexed option does not have an index");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--repo-storage-ca-file");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"option 'repo-storage-ca-file' must have an index");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--repo-azure-ca-path");
|
||||
TEST_ERROR(
|
||||
configParse(storageTest, strLstSize(argList), strLstPtr(argList), false), OptionInvalidError,
|
||||
"deprecated option 'repo-azure-ca-path' must have an index");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("allow option parse without an index");
|
||||
|
||||
TEST_RESULT_BOOL(cfgParseOptionP(STRDEF("repo-storage-ca-file"), .ignoreMissingIndex = true).found, true, "option");
|
||||
TEST_RESULT_BOOL(
|
||||
cfgParseOptionP(STRDEF("repo-azure-ca-path"), .ignoreMissingIndex = true).found, true, "deprecated option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
strLstAddZ(argList, "--pg1-host");
|
||||
@ -1373,6 +1481,7 @@ testRun(void)
|
||||
hrnCfgArgKeyRawReset(argList, cfgOptPgHost, 1);
|
||||
hrnCfgArgKeyRawReset(argList, cfgOptPgHost, 3);
|
||||
hrnCfgArgRawReset(argList, cfgOptBackupStandby);
|
||||
strLstAddZ(argList, "--retention-ful=55"); // Partial match for deprecated option
|
||||
strLstAddZ(argList, TEST_COMMAND_BACKUP);
|
||||
|
||||
setenv("PGBACKRESTXXX_NOTHING", "xxx", true);
|
||||
@ -1418,6 +1527,7 @@ testRun(void)
|
||||
"[db]\n"
|
||||
"pg1-host=db\n"
|
||||
"pg1-path=/path/to/db\n"
|
||||
"pg256-path=/path/to/db256\n"
|
||||
"%s=ignore\n"
|
||||
"%s=/path/to/db2\n"
|
||||
"pg3-host=ignore\n"
|
||||
@ -1445,11 +1555,13 @@ testRun(void)
|
||||
TEST_RESULT_UINT(cfgOptionGroupIdxToKey(cfgOptGrpPg, 1), 2, "pg2 is index 2");
|
||||
TEST_RESULT_STR_Z(cfgOptionStr(cfgOptPgPath), "/path/to/db", "default pg-path");
|
||||
TEST_RESULT_BOOL(cfgOptionGroupValid(cfgOptGrpPg), true, "pg group is valid");
|
||||
TEST_RESULT_UINT(cfgOptionGroupIdxTotal(cfgOptGrpPg), 2, "pg1 and pg2 are set");
|
||||
TEST_RESULT_UINT(cfgOptionGroupIdxTotal(cfgOptGrpPg), 3, "pg1, pg2, and pg256 are set");
|
||||
TEST_RESULT_BOOL(cfgOptionIdxBool(cfgOptPgLocal, 1), true, "pg2-local is set");
|
||||
TEST_RESULT_BOOL(cfgOptionIdxTest(cfgOptPgHost, 1), false, "pg2-host is not set (pg2-local override)");
|
||||
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgPath, cfgOptionKeyToIdx(cfgOptPgPath, 2)), "/path/to/db2", "pg2-path is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptPgPath), cfgSourceConfig, "pg1-path is source config");
|
||||
TEST_RESULT_STR_Z(
|
||||
cfgOptionIdxStr(cfgOptPgPath, cfgOptionKeyToIdx(cfgOptPgPath, 256)), "/path/to/db256", "pg256-path is set");
|
||||
TEST_RESULT_STR_Z(cfgOptionStr(cfgOptLockPath), "/", "lock-path is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptLockPath), cfgSourceConfig, "lock-path is source config");
|
||||
TEST_RESULT_STR_Z(cfgOptionIdxStr(cfgOptPgSocketPath, 0), "/path/to/socket", "pg1-socket-path is set");
|
||||
@ -1467,6 +1579,7 @@ testRun(void)
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptRepoHardlink), true, "repo-hardlink is set");
|
||||
TEST_RESULT_STR_Z(cfgOptionDisplay(cfgOptRepoHardlink), "true", "repo-hardlink display is true");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptRepoHardlink), cfgSourceConfig, "repo-hardlink is source config");
|
||||
TEST_RESULT_UINT(cfgOptionUInt(cfgOptRepoRetentionFull), 55, "repo-retention-full is set");
|
||||
TEST_RESULT_INT(cfgOptionInt(cfgOptCompressLevel), 3, "compress-level is set");
|
||||
TEST_RESULT_INT(cfgOptionSource(cfgOptCompressLevel), cfgSourceConfig, "compress-level is source config");
|
||||
TEST_RESULT_BOOL(cfgOptionBool(cfgOptBackupStandby), false, "backup-standby not is set");
|
||||
@ -1740,7 +1853,7 @@ testRun(void)
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "test");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, "/pg1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 8, "/pg8");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 128, "/pg128");
|
||||
hrnCfgArgRawZ(argList, cfgOptPg, "4");
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdBackup, argList, .role = cfgCmdRoleLocal), OptionInvalidValueError,
|
||||
@ -1829,7 +1942,7 @@ testRun(void)
|
||||
testOptionFind("db-ssh-port", cfgOptPgHostPort, 0, false, false, true);
|
||||
testOptionFind("db-user", cfgOptPgHostUser, 0, false, false, true);
|
||||
|
||||
TEST_RESULT_BOOL(cfgParseOptionP(STR("no-db-user")).found, false, "no-db-user not found");
|
||||
TEST_ERROR(cfgParseOptionP(STR("no-db-user")), OptionInvalidError, "option 'no-db-user' cannot be negated");
|
||||
|
||||
// Only check 1-8 since 8 was the max index when these option names were deprecated
|
||||
for (unsigned int optionIdx = 0; optionIdx < 8; optionIdx++)
|
||||
|
@ -40,9 +40,9 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptProtocolTimeout, "20");
|
||||
hrnCfgArgRawZ(argList, cfgOptBufferSize, "16384");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 2, "localhost");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHostUser, 2, TEST_USER);
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 2, TEST_PATH "/pg2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 256, "localhost");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgHostUser, 256, TEST_USER);
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 256, TEST_PATH "/pg256");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoPath, TEST_PATH "/repo");
|
||||
HRN_CFG_LOAD(cfgCmdBackup, argList);
|
||||
|
||||
@ -54,10 +54,10 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptProtocolTimeout, "20");
|
||||
hrnCfgArgRawFmt(argList, cfgOptBufferSize, "%zu", ioBufferSize());
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, TEST_PATH "/pg");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoHost, "localhost");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoHostUser, TEST_USER);
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoPath, TEST_PATH "/repo");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoHost, 128, "localhost");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostUser, 128, TEST_USER);
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 128, TEST_PATH "/repo128");
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "128");
|
||||
HRN_CFG_LOAD(cfgCmdArchiveGet, argList, .role = cfgCmdRoleLocal);
|
||||
|
||||
const Storage *const storageRepoWrite = storageRepoGet(0, true);
|
||||
@ -77,9 +77,9 @@ testRun(void)
|
||||
TEST_RESULT_UINT(storageInterface(storageRepoWrite).feature, storageInterface(storageTest).feature, "check features");
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoWrite, storageFeaturePath), true, "check path feature");
|
||||
TEST_RESULT_BOOL(storageFeature(storageRepoWrite, storageFeatureCompress), true, "check compress feature");
|
||||
TEST_RESULT_STR_Z(storagePathP(storageRepo, NULL), TEST_PATH "/repo", "check repo path");
|
||||
TEST_RESULT_STR_Z(storagePathP(storageRepoWrite, NULL), TEST_PATH "/repo", "check repo write path");
|
||||
TEST_RESULT_STR_Z(storagePathP(storagePgWrite, NULL), TEST_PATH "/pg2", "check pg write path");
|
||||
TEST_RESULT_STR_Z(storagePathP(storageRepo, NULL), TEST_PATH "/repo128", "check repo path");
|
||||
TEST_RESULT_STR_Z(storagePathP(storageRepoWrite, NULL), TEST_PATH "/repo128", "check repo write path");
|
||||
TEST_RESULT_STR_Z(storagePathP(storagePgWrite, NULL), TEST_PATH "/pg256", "check pg write path");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
@ -90,14 +90,14 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
storageInfoP(storageRepo, STRDEF(BOGUS_STR)), FileOpenError,
|
||||
"unable to get info for missing path/file '" TEST_PATH "/repo/BOGUS'");
|
||||
"unable to get info for missing path/file '" TEST_PATH "/repo128/BOGUS'");
|
||||
TEST_RESULT_BOOL(storageInfoP(storageRepo, STRDEF(BOGUS_STR), .ignoreMissing = true).exists, false, "missing file/path");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("path info");
|
||||
|
||||
storagePathCreateP(storageTest, STRDEF("repo"));
|
||||
HRN_STORAGE_TIME(storageTest, "repo", 1555160000);
|
||||
storagePathCreateP(storageTest, STRDEF("repo128"));
|
||||
HRN_STORAGE_TIME(storageTest, "repo128", 1555160000);
|
||||
|
||||
StorageInfo info = {.exists = false};
|
||||
TEST_ASSIGN(info, storageInfoP(storageRepo, NULL), "valid path");
|
||||
@ -145,7 +145,7 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("special info");
|
||||
|
||||
HRN_SYSTEM("mkfifo -m 666 " TEST_PATH "/repo/fifo");
|
||||
HRN_SYSTEM("mkfifo -m 666 " TEST_PATH "/repo128/fifo");
|
||||
|
||||
TEST_ASSIGN(info, storageInfoP(storageRepo, STRDEF("fifo")), "valid fifo");
|
||||
TEST_RESULT_STR(info.name, NULL, "name is not set");
|
||||
@ -162,7 +162,7 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("link info");
|
||||
|
||||
HRN_SYSTEM("ln -s ../repo/test " TEST_PATH "/repo/link");
|
||||
HRN_SYSTEM("ln -s ../repo128/test " TEST_PATH "/repo128/link");
|
||||
|
||||
TEST_ASSIGN(info, storageInfoP(storageRepo, STRDEF("link")), "valid link");
|
||||
TEST_RESULT_STR(info.name, NULL, "name is not set");
|
||||
@ -170,7 +170,7 @@ testRun(void)
|
||||
TEST_RESULT_INT(info.type, storageTypeLink, "check type");
|
||||
TEST_RESULT_UINT(info.size, 0, "check size");
|
||||
TEST_RESULT_INT(info.mode, 0777, "check mode");
|
||||
TEST_RESULT_STR_Z(info.linkDestination, "../repo/test", "check link destination");
|
||||
TEST_RESULT_STR_Z(info.linkDestination, "../repo128/test", "check link destination");
|
||||
TEST_RESULT_UINT(info.userId, TEST_USER_ID, "check user id");
|
||||
TEST_RESULT_STR(info.user, TEST_USER_STR, "check user");
|
||||
TEST_RESULT_UINT(info.groupId, TEST_GROUP_ID, "check group id");
|
||||
@ -241,12 +241,12 @@ testRun(void)
|
||||
|
||||
TEST_ERROR_FMT(
|
||||
strZ(strNewBuf(storageGetP(storageNewReadP(storagePgWrite, STRDEF("test.txt"))))), FileMissingError,
|
||||
"raised from remote-0 shim protocol: " STORAGE_ERROR_READ_MISSING, TEST_PATH "/pg2/test.txt");
|
||||
"raised from remote-0 shim protocol: " STORAGE_ERROR_READ_MISSING, TEST_PATH "/pg256/test.txt");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("read file without compression");
|
||||
|
||||
HRN_STORAGE_PUT(storageTest, TEST_PATH "/repo/test.txt", contentBuf);
|
||||
HRN_STORAGE_PUT(storageTest, TEST_PATH "/repo128/test.txt", contentBuf);
|
||||
|
||||
// Disable protocol compression in the storage object
|
||||
((StorageRemote *)storageDriver(storageRepo))->compressLevel = 0;
|
||||
@ -255,7 +255,7 @@ testRun(void)
|
||||
TEST_ASSIGN(fileRead, storageNewReadP(storageRepo, STRDEF("test.txt")), "new file");
|
||||
TEST_RESULT_BOOL(bufEq(storageGetP(fileRead), contentBuf), true, "get file");
|
||||
TEST_RESULT_BOOL(storageReadIgnoreMissing(fileRead), false, "check ignore missing");
|
||||
TEST_RESULT_STR_Z(storageReadName(fileRead), TEST_PATH "/repo/test.txt", "check name");
|
||||
TEST_RESULT_STR_Z(storageReadName(fileRead), TEST_PATH "/repo128/test.txt", "check name");
|
||||
TEST_RESULT_UINT(storageReadRemote(fileRead->driver, bufNew(32), false), 0, "nothing more to read");
|
||||
TEST_RESULT_UINT(((StorageReadRemote *)fileRead->driver)->protocolReadBytes, bufSize(contentBuf), "check read size");
|
||||
|
||||
@ -287,9 +287,10 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("read with filters");
|
||||
|
||||
HRN_STORAGE_PUT_Z(storageTest, TEST_PATH "/repo/test.txt", "TESTDATA!");
|
||||
HRN_STORAGE_PUT_Z(storageTest, TEST_PATH "/repo128/test.txt", "TESTDATA!");
|
||||
|
||||
TEST_ASSIGN(fileRead, storageNewReadP(storageRepo, STRDEF(TEST_PATH "/repo/test.txt"), .limit = VARUINT64(8)), "new read");
|
||||
TEST_ASSIGN(
|
||||
fileRead, storageNewReadP(storageRepo, STRDEF(TEST_PATH "/repo128/test.txt"), .limit = VARUINT64(8)), "new read");
|
||||
|
||||
IoFilterGroup *filterGroup = ioReadFilterGroup(storageReadIo(fileRead));
|
||||
ioFilterGroupAdd(filterGroup, ioSizeNew());
|
||||
@ -313,9 +314,10 @@ testRun(void)
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("read into sink (no data returned)");
|
||||
|
||||
HRN_STORAGE_PUT_Z(storageTest, TEST_PATH "/repo/test.txt", "TESTDATA");
|
||||
HRN_STORAGE_PUT_Z(storageTest, TEST_PATH "/repo128/test.txt", "TESTDATA");
|
||||
|
||||
TEST_ASSIGN(fileRead, storageNewReadP(storageRepo, STRDEF(TEST_PATH "/repo/test.txt"), .limit = VARUINT64(8)), "new read");
|
||||
TEST_ASSIGN(
|
||||
fileRead, storageNewReadP(storageRepo, STRDEF(TEST_PATH "/repo128/test.txt"), .limit = VARUINT64(8)), "new read");
|
||||
|
||||
filterGroup = ioReadFilterGroup(storageReadIo(fileRead));
|
||||
ioFilterGroupAdd(filterGroup, ioSizeNew());
|
||||
@ -355,7 +357,7 @@ testRun(void)
|
||||
TEST_RESULT_BOOL(storageWriteCreatePath(write), true, "path will be created");
|
||||
TEST_RESULT_UINT(storageWriteModeFile(write), STORAGE_MODE_FILE_DEFAULT, "file mode is default");
|
||||
TEST_RESULT_UINT(storageWriteModePath(write), STORAGE_MODE_PATH_DEFAULT, "path mode is default");
|
||||
TEST_RESULT_STR_Z(storageWriteName(write), TEST_PATH "/repo/test.txt", "check file name");
|
||||
TEST_RESULT_STR_Z(storageWriteName(write), TEST_PATH "/repo128/test.txt", "check file name");
|
||||
TEST_RESULT_BOOL(storageWriteSyncFile(write), true, "file is synced");
|
||||
TEST_RESULT_BOOL(storageWriteSyncPath(write), true, "path is synced");
|
||||
|
||||
@ -390,7 +392,7 @@ testRun(void)
|
||||
TEST_RESULT_VOID(storageWriteFree(write), "free file");
|
||||
|
||||
TEST_RESULT_UINT(
|
||||
storageInfoP(storageTest, STRDEF("repo/test2.txt.pgbackrest.tmp")).size, 16384, "file exists and is partial");
|
||||
storageInfoP(storageTest, STRDEF("repo128/test2.txt.pgbackrest.tmp")).size, 16384, "file exists and is partial");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("write the file again with protocol compression");
|
||||
@ -413,7 +415,7 @@ testRun(void)
|
||||
// Check the repo via the local test storage to ensure the remote created it.
|
||||
TEST_RESULT_VOID(storagePathCreateP(storageRepoWrite, path), "new path");
|
||||
StorageInfo info = {0};
|
||||
TEST_ASSIGN(info, storageInfoP(storageTest, strNewFmt("repo/%s", strZ(path))), "get path info");
|
||||
TEST_ASSIGN(info, storageInfoP(storageTest, strNewFmt("repo128/%s", strZ(path))), "get path info");
|
||||
TEST_RESULT_BOOL(info.exists, true, "path exists");
|
||||
TEST_RESULT_INT(info.mode, STORAGE_MODE_PATH_DEFAULT, "mode is default");
|
||||
|
||||
@ -422,14 +424,14 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(
|
||||
storagePathCreateP(storageRepoWrite, STRDEF("testpath"), .errorOnExists = true), PathCreateError,
|
||||
"raised from remote-0 shim protocol: unable to create path '" TEST_PATH "/repo/testpath': [17] File exists");
|
||||
"raised from remote-0 shim protocol: unable to create path '" TEST_PATH "/repo128/testpath': [17] File exists");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on missing parent path");
|
||||
|
||||
TEST_ERROR(
|
||||
storagePathCreateP(storageRepoWrite, STRDEF("parent/testpath"), .noParentCreate = true), PathCreateError,
|
||||
"raised from remote-0 shim protocol: unable to create path '" TEST_PATH "/repo/parent/testpath': [2] No such"
|
||||
"raised from remote-0 shim protocol: unable to create path '" TEST_PATH "/repo128/parent/testpath': [2] No such"
|
||||
" file or directory");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -441,7 +443,7 @@ testRun(void)
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
storageInfoListP(
|
||||
storageRepo, STRDEF(TEST_PATH "/repo/parent"), hrnStorageInfoListCallback, &callbackData,
|
||||
storageRepo, STRDEF(TEST_PATH "/repo128/parent"), hrnStorageInfoListCallback, &callbackData,
|
||||
.sortOrder = sortOrderAsc),
|
||||
true, "info list");
|
||||
TEST_RESULT_STR_Z(
|
||||
@ -455,16 +457,16 @@ testRun(void)
|
||||
if (testBegin("storagePathRemove()"))
|
||||
{
|
||||
const String *path = STRDEF("testpath");
|
||||
storagePathCreateP(storageTest, STRDEF("repo"));
|
||||
storagePathCreateP(storageTest, STRDEF("repo128"));
|
||||
TEST_RESULT_VOID(storagePathCreateP(storageRepoWrite, path), "new path");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("remote remove path");
|
||||
|
||||
// Check the repo via the local test storage to ensure the remote wrote it, then remove via the remote and confirm removed
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo/%s", strZ(path))), true, "path exists");
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo128/%s", strZ(path))), true, "path exists");
|
||||
TEST_RESULT_VOID(storagePathRemoveP(storageRepoWrite, path), "remote remove path");
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo/%s", strZ(path))), false, "path removed");
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo128/%s", strZ(path))), false, "path removed");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("remove recursive");
|
||||
@ -474,13 +476,13 @@ testRun(void)
|
||||
"new path and file");
|
||||
|
||||
TEST_RESULT_VOID(storagePathRemoveP(storageRepoWrite, STRDEF("testpath"), .recurse = true), "remove missing path");
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo/%s", strZ(path))), false, "recurse path removed");
|
||||
TEST_RESULT_BOOL(storagePathExistsP(storageTest, strNewFmt("repo128/%s", strZ(path))), false, "recurse path removed");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("storageRemove()"))
|
||||
{
|
||||
storagePathCreateP(storageTest, STRDEF("repo"));
|
||||
storagePathCreateP(storageTest, STRDEF("repo128"));
|
||||
|
||||
const String *file = STRDEF("file.txt");
|
||||
|
||||
@ -491,16 +493,16 @@ testRun(void)
|
||||
TEST_RESULT_VOID(storagePutP(storageNewWriteP(storageRepoWrite, file), BUFSTRDEF("TEST")), "new file");
|
||||
|
||||
// Check the repo via the local test storage to ensure the remote wrote it, then remove via the remote and confirm removed
|
||||
TEST_RESULT_BOOL(storageExistsP(storageTest, strNewFmt("repo/%s", strZ(file))), true, "file exists");
|
||||
TEST_RESULT_BOOL(storageExistsP(storageTest, strNewFmt("repo128/%s", strZ(file))), true, "file exists");
|
||||
TEST_RESULT_VOID(storageRemoveP(storageRepoWrite, file), "remote remove file");
|
||||
TEST_RESULT_BOOL(storageExistsP(storageTest, strNewFmt("repo/%s", strZ(file))), false, "file removed");
|
||||
TEST_RESULT_BOOL(storageExistsP(storageTest, strNewFmt("repo128/%s", strZ(file))), false, "file removed");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on missing file");
|
||||
|
||||
TEST_ERROR(
|
||||
storageRemoveP(storageRepoWrite, file, .errorOnMissing = true), FileRemoveError,
|
||||
"raised from remote-0 shim protocol: unable to remove '" TEST_PATH "/repo/file.txt': [2] No such file or directory");
|
||||
"raised from remote-0 shim protocol: unable to remove '" TEST_PATH "/repo128/file.txt': [2] No such file or directory");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("ignore missing file");
|
||||
@ -511,7 +513,7 @@ testRun(void)
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("storagePathSync()"))
|
||||
{
|
||||
storagePathCreateP(storageTest, STRDEF("repo"));
|
||||
storagePathCreateP(storageTest, STRDEF("repo128"));
|
||||
|
||||
const String *path = STRDEF("testpath");
|
||||
TEST_RESULT_VOID(storagePathCreateP(storageRepoWrite, path), "new path");
|
||||
|
Reference in New Issue
Block a user