diff --git a/doc/xml/release.xml b/doc/xml/release.xml index 5911368c6..ec7bb9d5a 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -62,7 +62,7 @@ -

Config parsing implemented in C.

+

Config parsing implemented in C and passed to Perl as JSON.

diff --git a/lib/pgBackRest/Archive/Get/Get.pm b/lib/pgBackRest/Archive/Get/Get.pm index a976b69f9..1cf744d20 100644 --- a/lib/pgBackRest/Archive/Get/Get.pm +++ b/lib/pgBackRest/Archive/Get/Get.pm @@ -39,7 +39,18 @@ sub process my $self = shift; # Assign function parameters, defaults, and log debug info - my ($strOperation) = logDebugParam(__PACKAGE__ . '->process'); + my + ( + $strOperation, + $strSourceArchive, + $strDestinationFile + ) = + logDebugParam + ( + __PACKAGE__ . '->process', \@_, + {name => 'strSourceArchive'}, + {name => 'strDestinationFile'} + ); # Make sure the command happens on the db side if (!isDbLocal()) @@ -48,25 +59,25 @@ sub process } # Make sure the archive file is defined - if (!defined($ARGV[1])) + if (!defined($strSourceArchive)) { confess &log(ERROR, 'WAL segment not provided', ERROR_PARAM_REQUIRED); } # Make sure the destination file is defined - if (!defined($ARGV[2])) + if (!defined($strDestinationFile)) { confess &log(ERROR, 'WAL segment destination not provided', ERROR_PARAM_REQUIRED); } # Info for the Postgres log - &log(INFO, 'get WAL segment ' . $ARGV[1]); + &log(INFO, 'get WAL segment ' . $strSourceArchive); # Return from function and log return values if any return logDebugReturn ( $strOperation, - {name => 'iResult', value => $self->get($ARGV[1], $ARGV[2]), trace => true} + {name => 'iResult', value => $self->get($strSourceArchive, $strDestinationFile), trace => true} ); } diff --git a/lib/pgBackRest/Config/Config.pm b/lib/pgBackRest/Config/Config.pm index 5c7c3c878..ca4c64fa4 100644 --- a/lib/pgBackRest/Config/Config.pm +++ b/lib/pgBackRest/Config/Config.pm @@ -6,13 +6,11 @@ package pgBackRest::Config::Config; use strict; use warnings FATAL => qw(all); use Carp qw(confess); +use English '-no_match_vars'; -use Cwd qw(abs_path); use Exporter qw(import); our @EXPORT = qw(); -use File::Basename qw(dirname basename); -use Getopt::Long qw(GetOptions); -use Storable qw(dclone); +use JSON::PP; use pgBackRest::Common::Exception; use pgBackRest::Common::Ini; @@ -83,63 +81,79 @@ push @EXPORT, qw(configLogging); sub configLoad { my $bInitLogging = shift; + my $strBackRestBin = shift; + my $strCommandName = shift; + my $strConfigJson = shift; # Clear option in case it was loaded before %oOption = (); - # Build hash with all valid command-line options - my @stryOptionAllow; + # Set backrest bin + backrestBinSet($strBackRestBin); + # Set command + $strCommand = $strCommandName; + + # Convert options from JSON to a hash + my $rhOption; + + eval + { + $rhOption = (JSON::PP->new()->allow_nonref())->decode($strConfigJson); + return true; + } + or do + { + confess &log(ASSERT, "$EVAL_ERROR" . (defined($strConfigJson) ? ":\n${strConfigJson}" : "")); + }; + + # Load options into final option hash for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++) { - my $strKey = cfgOptionName($iOptionId); + my $strOptionName = cfgOptionName($iOptionId); - foreach my $bAltName (false, true) + # If option is not defined then it is not valid + if (!defined($rhOption->{$strOptionName})) { - my $strOptionName = $strKey; + $oOption{$strOptionName}{valid} = false; + } + # Else set option + else + { + $oOption{$strOptionName}{valid} = true; + $oOption{$strOptionName}{source} = + defined($rhOption->{$strOptionName}{source}) ? $rhOption->{$strOptionName}{source} : CFGDEF_SOURCE_DEFAULT; - if ($bAltName) + # If option is negated only boolean will have a value + if ($rhOption->{$strOptionName}{negate}) { - if (!defined(cfgDefOptionNameAlt($iOptionId))) + $oOption{$strOptionName}{negate} = true; + + if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN) { - next; + $oOption{$strOptionName}{value} = false; } - - $strOptionName = cfgDefOptionNameAlt($iOptionId); } - - my $strOption = $strOptionName; - - if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH || cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_LIST) + # Else set the value + else { - $strOption .= '=s@'; - } - elsif (cfgDefOptionType($iOptionId) ne CFGDEF_TYPE_BOOLEAN) - { - $strOption .= '=s'; - } + $oOption{$strOptionName}{negate} = false; - push(@stryOptionAllow, $strOption); - - # Check if the option can be negated - if (cfgDefOptionNegate($iOptionId)) - { - push(@stryOptionAllow, 'no-' . $strOptionName); + if (defined($rhOption->{$strOptionName}{value})) + { + if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN) + { + $oOption{$strOptionName}{value} = $rhOption->{$strOptionName}{value} eq INI_TRUE ? true : false; + } + else + { + $oOption{$strOptionName}{value} = $rhOption->{$strOptionName}{value}; + } + } } } } - # Get command-line options - my %oOptionTest; - - # Parse command line options - if (!GetOptions(\%oOptionTest, @stryOptionAllow)) - { - confess &log(ASSERT, "error parsing command line"); - } - - optionValidate(\%oOptionTest); - # If this is not the remote and logging is allowed (to not overwrite log levels for tests) then set the log level so that # INFO/WARN messages can be displayed (the user may still disable them). This should be run before any WARN logging is # generated. @@ -275,594 +289,6 @@ sub configLoad push @EXPORT, qw(configLoad); -#################################################################################################################################### -# optionValueGet -# -# Find the value of an option using both the regular and alt values. Error if both are defined. -#################################################################################################################################### -sub optionValueGet -{ - my $strOption = shift; - my $hOption = shift; - - my $strValue = $hOption->{$strOption}; - - # Some options have an alternate name so check for that as well - my $iOptionId = cfgOptionId($strOption); - - if (defined(cfgDefOptionNameAlt($iOptionId))) - { - my $strOptionAlt = cfgDefOptionNameAlt($iOptionId); - my $strValueAlt = $hOption->{$strOptionAlt}; - - if (defined($strValueAlt)) - { - if (!defined($strValue)) - { - $strValue = $strValueAlt; - - delete($hOption->{$strOptionAlt}); - $hOption->{$strOption} = $strValue; - } - else - { - confess &log(ERROR, "'${strOption}' and '${strOptionAlt}' cannot both be defined", ERROR_OPTION_INVALID_VALUE); - } - } - } - - return $strValue; -} - -#################################################################################################################################### -# optionValidate -# -# Make sure the command-line options are valid based on the command. -#################################################################################################################################### -sub optionValidate -{ - my $oOptionTest = shift; - - # Check that the command is present and valid - $strCommand = $ARGV[0]; - - if (!defined($strCommand)) - { - confess &log(ERROR, "command must be specified", ERROR_COMMAND_REQUIRED); - } - - my $iCommandId = cfgCommandId($strCommand); - - if ($iCommandId eq "-1") - { - confess &log(ERROR, "invalid command ${strCommand}", ERROR_COMMAND_INVALID); - } - - # Hash to store contents of the config file. The file will be loaded once the config dependency is resolved unless all options - # are set on the command line or --no-config is specified. - my $oConfig; - my $bConfigExists = true; - - # Keep track of unresolved dependencies - my $bDependUnresolved = true; - my %oOptionResolved; - - # Loop through all possible options - while ($bDependUnresolved) - { - # Assume that all dependencies will be resolved in this loop - $bDependUnresolved = false; - - for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++) - { - my $strOption = cfgOptionName($iOptionId); - - # Skip the option if it has been resolved in a prior loop - if (defined($oOptionResolved{$strOption})) - { - next; - } - - # Determine if an option is valid for a command - $oOption{$strOption}{valid} = cfgDefOptionValid($iCommandId, $iOptionId); - - if (!$oOption{$strOption}{valid}) - { - $oOptionResolved{$strOption} = true; - next; - } - - # Store the option value - my $strValue = optionValueGet($strOption, $oOptionTest); - - # Check to see if an option can be negated. Make sure that it is not set and negated at the same time. - $oOption{$strOption}{negate} = false; - - if (cfgDefOptionNegate($iOptionId)) - { - $oOption{$strOption}{negate} = defined($$oOptionTest{'no-' . $strOption}); - - if ($oOption{$strOption}{negate} && defined($strValue)) - { - confess &log(ERROR, "option '${strOption}' cannot be both set and negated", ERROR_OPTION_NEGATE); - } - - if ($oOption{$strOption}{negate} && cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN) - { - $strValue = false; - } - } - - # Check dependency for the command then for the option - my $bDependResolved = true; - my $strDependOption; - my $strDependValue; - my $strDependType; - - if (cfgDefOptionDepend($iCommandId, $iOptionId)) - { - # Check if the depend option has a value - my $iDependOptionId = cfgDefOptionDependOption($iCommandId, $iOptionId); - $strDependOption = cfgOptionName($iDependOptionId); - $strDependValue = $oOption{$strDependOption}{value}; - - # Make sure the depend option has been resolved, otherwise skip this option for now - if (!defined($oOptionResolved{$strDependOption})) - { - $bDependUnresolved = true; - next; - } - - if (!defined($strDependValue)) - { - $bDependResolved = false; - $strDependType = 'source'; - } - - # If a depend value exists, make sure the option value matches - if ($bDependResolved && cfgDefOptionDependValueTotal($iCommandId, $iOptionId) == 1 && - cfgDefOptionDependValue($iCommandId, $iOptionId, 0) ne $strDependValue) - { - $bDependResolved = false; - $strDependType = 'value'; - } - - # If a depend list exists, make sure the value is in the list - if ($bDependResolved && cfgDefOptionDependValueTotal($iCommandId, $iOptionId) > 1 && - !cfgDefOptionDependValueValid($iCommandId, $iOptionId, $strDependValue)) - { - $bDependResolved = false; - $strDependType = 'list'; - } - } - - # If the option value is undefined and not negated, see if it can be loaded from the config file - if (!defined($strValue) && !$oOption{$strOption}{negate} && $strOption ne cfgOptionName(CFGOPT_CONFIG) && - defined(cfgDefOptionSection($iOptionId)) && $bDependResolved) - { - # If the config option has not been resolved yet then continue processing - if (!defined($oOptionResolved{cfgOptionName(CFGOPT_CONFIG)}) || - !defined($oOptionResolved{cfgOptionName(CFGOPT_STANZA)})) - { - $bDependUnresolved = true; - next; - } - - # If the config option is defined try to get the option from the config file - if ($bConfigExists && defined($oOption{cfgOptionName(CFGOPT_CONFIG)}{value})) - { - # Attempt to load the config file if it has not been loaded - if (!defined($oConfig)) - { - my $strConfigFile = $oOption{cfgOptionName(CFGOPT_CONFIG)}{value}; - $bConfigExists = -e $strConfigFile; - - if ($bConfigExists) - { - if (!-f $strConfigFile) - { - confess &log(ERROR, "'${strConfigFile}' is not a file", ERROR_FILE_INVALID); - } - - # Load Storage::Helper module - require pgBackRest::Storage::Helper; - pgBackRest::Storage::Helper->import(); - - $oConfig = iniParse(${storageLocal->('/')->get($strConfigFile)}, {bRelaxed => true}); - } - } - - # Get the section that the value should be in - my $strSection = cfgDefOptionSection($iOptionId); - - # Always check for the option in the stanza section first - if (cfgOptionTest(CFGOPT_STANZA)) - { - $strValue = optionValueGet($strOption, $$oConfig{cfgOption(CFGOPT_STANZA)}); - } - - # Only continue searching when strSection != CFGDEF_SECTION_STANZA. Some options (e.g. db-path) can only be - # configured in the stanza section. - if (!defined($strValue) && $strSection ne CFGDEF_SECTION_STANZA) - { - # Check the stanza command section - if (cfgOptionTest(CFGOPT_STANZA)) - { - $strValue = optionValueGet($strOption, $$oConfig{cfgOption(CFGOPT_STANZA) . ":${strCommand}"}); - } - - # Check the global command section - if (!defined($strValue)) - { - $strValue = optionValueGet($strOption, $$oConfig{&CFGDEF_SECTION_GLOBAL . ":${strCommand}"}); - } - - # Finally check the global section - if (!defined($strValue)) - { - $strValue = optionValueGet($strOption, $$oConfig{&CFGDEF_SECTION_GLOBAL}); - } - } - - # Fix up data types - if (defined($strValue)) - { - # The empty string is undefined - if ($strValue eq '') - { - $strValue = undef; - } - # Convert Y or N to boolean - elsif (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN) - { - if ($strValue eq 'y') - { - $strValue = true; - } - elsif ($strValue eq 'n') - { - $strValue = false; - } - else - { - confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", - ERROR_OPTION_INVALID_VALUE); - } - } - # Convert a list of key/value pairs to a hash - elsif (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH || - cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_LIST) - { - my @oValue = (); - - # If there is only one key/value - if (ref(\$strValue) eq 'SCALAR') - { - push(@oValue, $strValue); - } - # Else if there is an array of values - else - { - @oValue = @{$strValue}; - } - - # Reset the value hash - $strValue = {}; - - # Iterate and parse each key/value pair - foreach my $strHash (@oValue) - { - my $iEqualIdx = index($strHash, '='); - - if ($iEqualIdx < 1 || $iEqualIdx == length($strHash) - 1) - { - confess &log(ERROR, "'${strHash}' is not valid for '${strOption}' option", - ERROR_OPTION_INVALID_VALUE); - } - - my $strHashKey = substr($strHash, 0, $iEqualIdx); - my $strHashValue = substr($strHash, length($strHashKey) + 1); - - $$strValue{$strHashKey} = $strHashValue; - } - } - # In all other cases the value should be scalar - elsif (ref(\$strValue) ne 'SCALAR') - { - confess &log( - ERROR, "option '${strOption}' cannot be specified multiple times", ERROR_OPTION_MULTIPLE_VALUE); - } - - $oOption{$strOption}{source} = CFGDEF_SOURCE_CONFIG; - } - } - } - - if (cfgDefOptionDepend($iCommandId, $iOptionId) && !$bDependResolved && defined($strValue)) - { - my $strError = "option '${strOption}' not valid without option "; - my $iDependOptionId = cfgOptionId($strDependOption); - - if ($strDependType eq 'source') - { - confess &log(ERROR, "${strError}'${strDependOption}'", ERROR_OPTION_INVALID); - } - - # If a depend value exists, make sure the option value matches - if ($strDependType eq 'value') - { - if (cfgDefOptionType($iDependOptionId) eq CFGDEF_TYPE_BOOLEAN) - { - $strError .= - "'" . (cfgDefOptionDependValue($iCommandId, $iOptionId, 0) ? '' : 'no-') . "${strDependOption}'"; - } - else - { - $strError .= "'${strDependOption}' = '" . cfgDefOptionDependValue($iCommandId, $iOptionId, 0) . "'"; - } - - confess &log(ERROR, $strError, ERROR_OPTION_INVALID); - } - - $strError .= "'${strDependOption}'"; - - # If a depend list exists, make sure the value is in the list - if ($strDependType eq 'list') - { - my @oyValue; - - for (my $iValueId = 0; $iValueId < cfgDefOptionDependValueTotal($iCommandId, $iOptionId); $iValueId++) - { - push(@oyValue, "'" . cfgDefOptionDependValue($iCommandId, $iOptionId, $iValueId) . "'"); - } - - $strError .= @oyValue == 1 ? " = $oyValue[0]" : " in (" . join(", ", @oyValue) . ")"; - confess &log(ERROR, $strError, ERROR_OPTION_INVALID); - } - } - - # Is the option defined? - if (defined($strValue)) - { - # Check that floats and integers are valid - if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_INTEGER || - cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_FLOAT) - { - # Test that the string is a valid float or integer by adding 1 to it. It's pretty hokey but it works and it - # beats requiring Scalar::Util::Numeric to do it properly. - my $bError = false; - - eval - { - my $strTest = $strValue + 1; - return true; - } - or do - { - $bError = true; - }; - - # Check that integers are really integers - if (!$bError && cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_INTEGER && - (int($strValue) . 'S') ne ($strValue . 'S')) - { - $bError = true; - } - - # Error if the value did not pass tests - !$bError - or confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE); - } - - # Process an allow list for the command then for the option - if (cfgDefOptionAllowList($iCommandId, $iOptionId) && - !cfgDefOptionAllowListValueValid($iCommandId, $iOptionId, $strValue)) - { - confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE); - } - - # Process an allow range for the command then for the option - if (cfgDefOptionAllowRange($iCommandId, $iOptionId) && - ($strValue < cfgDefOptionAllowRangeMin($iCommandId, $iOptionId) || - $strValue > cfgDefOptionAllowRangeMax($iCommandId, $iOptionId))) - { - confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_RANGE); - } - - # Set option value - if (ref($strValue) eq 'ARRAY' && - (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH || cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_LIST)) - { - foreach my $strItem (@{$strValue}) - { - my $strKey; - my $strValue; - - # If the keys are expected to have values - if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH) - { - # Check for = and make sure there is a least one character on each side - my $iEqualPos = index($strItem, '='); - - if ($iEqualPos < 1 || length($strItem) <= $iEqualPos + 1) - { - confess &log(ERROR, "'${strItem}' not valid key/value for '${strOption}' option", - ERROR_OPTION_INVALID_PAIR); - } - - $strKey = substr($strItem, 0, $iEqualPos); - $strValue = substr($strItem, $iEqualPos + 1); - } - # Else no values are expected so set value to true - else - { - $strKey = $strItem; - $strValue = true; - } - - # Check that the key has not already been set - if (defined($oOption{$strOption}{$strKey}{value})) - { - confess &log(ERROR, "'${$strItem}' already defined for '${strOption}' option", - ERROR_OPTION_DUPLICATE_KEY); - } - - # Set key/value - $oOption{$strOption}{value}{$strKey} = $strValue; - } - } - else - { - $oOption{$strOption}{value} = $strValue; - } - - # If not config sourced then it must be a param - if (!defined($oOption{$strOption}{source})) - { - $oOption{$strOption}{source} = CFGDEF_SOURCE_PARAM; - } - } - # Else try to set a default - elsif ($bDependResolved) - { - # Source is default for this option - $oOption{$strOption}{source} = CFGDEF_SOURCE_DEFAULT; - - # Check for default in command then option - my $strDefault = cfgDefOptionDefault($iCommandId, $iOptionId); - - # If default is defined - if (defined($strDefault)) - { - # Only set default if dependency is resolved - $oOption{$strOption}{value} = $strDefault if !$oOption{$strOption}{negate}; - } - # Else check required - elsif (cfgDefOptionRequired($iCommandId, $iOptionId)) - { - confess &log(ERROR, - "${strCommand} command requires option: ${strOption}" . - (defined(cfgDefOptionSection($iOptionId)) && - cfgDefOptionSection($iOptionId) eq CFGDEF_SECTION_STANZA ? "\nHINT: does this stanza exist?" : ''), - ERROR_OPTION_REQUIRED); - } - } - - $oOptionResolved{$strOption} = true; - } - } - - # Make sure all options specified on the command line are valid - foreach my $strOption (sort(keys(%{$oOptionTest}))) - { - # Strip "no-" off the option - $strOption = $strOption =~ /^no\-/ ? substr($strOption, 3) : $strOption; - - if (!$oOption{$strOption}{valid}) - { - confess &log(ERROR, "option '${strOption}' not valid for command '${strCommand}'", ERROR_OPTION_COMMAND); - } - } - - # If a config file was loaded then determine if all options are valid in the config file - if (defined($oConfig)) - { - configFileValidate($oConfig); - } -} - -#################################################################################################################################### -# configFileValidate -# -# Determine if the configuration file contains any invalid options or placements. Not valid on remote. -#################################################################################################################################### -sub configFileValidate -{ - my $oConfig = shift; - - my $bFileValid = true; - - if (!cfgCommandTest(CFGCMD_REMOTE) && !cfgCommandTest(CFGCMD_LOCAL)) - { - foreach my $strSectionKey (keys(%$oConfig)) - { - my ($strSection, $strCommand) = ($strSectionKey =~ m/([^:]*):*(\w*-*\w*)/); - - foreach my $strOption (keys(%{$$oConfig{$strSectionKey}})) - { - my $strOptionDisplay = $strOption; - my $strValue = $$oConfig{$strSectionKey}{$strOption}; - - # Is the option listed as an alternate name for another option? If so, replace it with the recognized option. - my $strOptionAltName = optionAltName($strOption); - - if (defined($strOptionAltName)) - { - $strOption = $strOptionAltName; - } - - # Is the option a valid pgbackrest option? - if (!(cfgOptionId($strOption) ne '-1' || defined($strOptionAltName))) - { - &log(WARN, cfgOption(CFGOPT_CONFIG) . " file contains invalid option '${strOptionDisplay}'"); - $bFileValid = false; - } - else - { - # Is the option valid for the command section in which it is located? - if (defined($strCommand) && $strCommand ne '') - { - if (!cfgDefOptionValid(cfgCommandId($strCommand), cfgOptionId($strOption))) - { - &log(WARN, cfgOption(CFGOPT_CONFIG) . " valid option '${strOptionDisplay}' is not valid for command " . - "'${strCommand}'"); - $bFileValid = false; - } - } - - # Is the valid option a stanza-only option and not located in a global section? - if (cfgDefOptionSection(cfgOptionId($strOption)) eq CFGDEF_SECTION_STANZA && - $strSection eq CFGDEF_SECTION_GLOBAL) - { - &log(WARN, - cfgOption(CFGOPT_CONFIG) . " valid option '${strOptionDisplay}' is a stanza section option and is" . - " not valid in section ${strSection}\n" . - "HINT: global options can be specified in global or stanza sections but not visa-versa"); - $bFileValid = false; - } - } - } - } - } - - return $bFileValid; -} - -#################################################################################################################################### -# optionAltName -# -# Returns the ALT_NAME for the option if one exists. -#################################################################################################################################### -sub optionAltName -{ - my $strOption = shift; - - my $strOptionAltName = undef; - - # Check if the options exists as an alternate name (e.g. db-host has altname db1-host) - for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++) - { - my $strKey = cfgOptionName($iOptionId); - - if (defined(cfgDefOptionNameAlt($iOptionId)) && cfgDefOptionNameAlt($iOptionId) eq $strOption) - { - $strOptionAltName = $strKey; - } - } - - return $strOptionAltName; -} - #################################################################################################################################### # cfgOptionIdFromIndex - return name for options that can be indexed (e.g. db1-host, db2-host). #################################################################################################################################### diff --git a/lib/pgBackRest/Main.pm b/lib/pgBackRest/Main.pm index edc695069..83d14a5a3 100644 --- a/lib/pgBackRest/Main.pm +++ b/lib/pgBackRest/Main.pm @@ -30,7 +30,9 @@ use pgBackRest::Version; sub main { my $strBackRestBin = shift; - @ARGV = @_; + my $strCommand = shift; + my $strConfigJson = shift; + my @stryCommandArg = @_; ################################################################################################################################ # Run in eval block to catch errors @@ -40,8 +42,7 @@ sub main ############################################################################################################################ # Load command line parameters and config ############################################################################################################################ - backrestBinSet($strBackRestBin); - configLoad(); + configLoad(undef, $strBackRestBin, $strCommand, $strConfigJson); # Set test options if (cfgOptionTest(CFGOPT_TEST) && cfgOption(CFGOPT_TEST)) @@ -58,7 +59,7 @@ sub main require pgBackRest::Archive::Push::Push; pgBackRest::Archive::Push::Push->import(); - exitSafe(new pgBackRest::Archive::Push::Push()->process($ARGV[1])); + exitSafe(new pgBackRest::Archive::Push::Push()->process($stryCommandArg[0])); } ############################################################################################################################ @@ -70,7 +71,7 @@ sub main require pgBackRest::Archive::Get::Get; pgBackRest::Archive::Get::Get->import(); - exitSafe(new pgBackRest::Archive::Get::Get()->process()); + exitSafe(new pgBackRest::Archive::Get::Get()->process($stryCommandArg[0], $stryCommandArg[1])); } ############################################################################################################################ diff --git a/src/common/log.c b/src/common/log.c index 60bee0c1e..57aca4928 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -16,7 +16,7 @@ Module variables ***********************************************************************************************************************************/ // Log levels LogLevel logLevelStdOut = logLevelOff; -LogLevel logLevelStdErr = logLevelWarn; +LogLevel logLevelStdErr = logLevelOff; // Log file handles int logHandleStdOut = STDOUT_FILENO; diff --git a/src/config/load.c b/src/config/load.c index c1d3b95b7..b3f40bbae 100644 --- a/src/config/load.c +++ b/src/config/load.c @@ -20,20 +20,23 @@ cfgLoad(int argListSize, const char *argList[]) configParse(argListSize, argList); // Initialize logging - LogLevel logLevelConsole = logLevelOff; - LogLevel logLevelStdErr = logLevelOff; - bool logTimestamp = true; + if (cfgCommand() != cfgCmdLocal && cfgCommand() != cfgCmdRemote) + { + LogLevel logLevelConsole = logLevelOff; + LogLevel logLevelStdErr = logLevelOff; + bool logTimestamp = true; - if (cfgOptionValid(cfgOptLogLevelConsole)) - logLevelConsole = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelConsole))); + if (cfgOptionValid(cfgOptLogLevelConsole)) + logLevelConsole = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelConsole))); - if (cfgOptionValid(cfgOptLogLevelStderr)) - logLevelStdErr = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelStderr))); + if (cfgOptionValid(cfgOptLogLevelStderr)) + logLevelStdErr = logLevelEnum(strPtr(cfgOptionStr(cfgOptLogLevelStderr))); - if (cfgOptionValid(cfgOptLogTimestamp)) - logTimestamp = cfgOptionBool(cfgOptLogTimestamp); + if (cfgOptionValid(cfgOptLogTimestamp)) + logTimestamp = cfgOptionBool(cfgOptLogTimestamp); - logInit(logLevelConsole, logLevelStdErr, logTimestamp); + logInit(logLevelConsole, logLevelStdErr, logTimestamp); + } // Set default for backup-cmd if (cfgOptionValid(cfgOptBackupHost) && cfgOption(cfgOptBackupHost) != NULL && diff --git a/src/config/parse.c b/src/config/parse.c index 2030ecdc0..730cfe5ab 100644 --- a/src/config/parse.c +++ b/src/config/parse.c @@ -8,6 +8,7 @@ Command and Option Parse #include "common/error.h" #include "common/ini.h" +#include "common/log.h" #include "common/memContext.h" #include "config/parse.h" #include "storage/helper.h" @@ -190,7 +191,12 @@ configParse(int argListSize, const char *argList[]) if (commandParamList != NULL) cfgCommandParamSet(commandParamList); - // Parse options from config file unless --no-config passed + // Enable logging except for local and remote commands + if (cfgCommand() != cfgCmdLocal && cfgCommand() != cfgCmdRemote) + logInit(logLevelOff, logLevelWarn, false); + + // Phase 2: parse config file unless --no-config passed + // --------------------------------------------------------------------------------------------------------------------- if (cfgCommand() != cfgCmdNone && cfgCommand() != cfgCmdVersion && cfgCommand() != cfgCmdHelp) @@ -198,8 +204,6 @@ configParse(int argListSize, const char *argList[]) // Get the command definition id ConfigDefineCommand commandDefId = cfgCommandDefIdFromId(cfgCommand()); - // Phase 2: parse config file - // --------------------------------------------------------------------------------------------------------------------- if (!parseOptionList[cfgOptConfig].negate) { // Get the config file name from the command-line if it exists else default @@ -263,13 +267,13 @@ configParse(int argListSize, const char *argList[]) // Warn if the option not found if (optionList[optionIdx].name == NULL) { - /// ??? Put warning here once there is a logging system + LOG_WARN("'%s' contains invalid option '%s'", strPtr(configFile), strPtr(key)); continue; } // Warn if negate option found in config else if (optionList[optionIdx].val & PARSE_NEGATE_FLAG) { - /// ??? Put warning here once there is a logging system + LOG_WARN("'%s' contains negate option '%s'", strPtr(configFile), strPtr(key)); continue; } @@ -279,7 +283,7 @@ configParse(int argListSize, const char *argList[]) /// Warn if this option should be command-line only if (cfgDefOptionSection(optionDefId) == cfgDefSectionCommandLine) { - /// ??? Put warning here once there is a logging system + LOG_WARN("'%s' contains command-line only option '%s'", strPtr(configFile), strPtr(key)); continue; } @@ -293,7 +297,9 @@ configParse(int argListSize, const char *argList[]) // Warn if it is in a command section if (sectionIdx % 2 == 0) { - // ??? Put warning here once there is a logging system (and remove continue and braces) + LOG_WARN( + "'%s' contains option '%s' invalid for section '%s'", strPtr(configFile), strPtr(key), + strPtr(section)); continue; } diff --git a/src/perl/exec.c b/src/perl/exec.c index e26403262..ebc619035 100644 --- a/src/perl/exec.c +++ b/src/perl/exec.c @@ -43,33 +43,57 @@ StringList *perlCommand() } // Construct option list to pass to main - String *mainCallParam = strNew(""); + String *configJson = strNew("{"); for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++) { - // Skip the option if it is not valid or not a command line option - if (!cfgOptionValid(optionId) || cfgOptionSource(optionId) != cfgSourceParam) + // Skip the option if it is not valid + if (!cfgOptionValid(optionId)) continue; + // Output option + if (strSize(configJson) != 1) + strCat(configJson, ","); + + strCatFmt(configJson, "\"%s\":{", cfgOptionName(optionId)); + + // Output source unless it is default + if (cfgOptionSource(optionId) != cfgSourceDefault) + { + strCat(configJson, "\"source\":\""); + + if (cfgOptionSource(optionId) == cfgSourceParam) + strCat(configJson, "param"); + else + strCat(configJson, "config"); + + strCat(configJson, "\""); + } + // If option was negated if (cfgOptionNegate(optionId)) - strCatFmt(mainCallParam, ", '--no-%s'", cfgOptionName(optionId)); - // Else not negated - else + strCatFmt(configJson, ",\"negate\":%s", strPtr(varStrForce(varNewBool(true)))); + // Else not negated and has a value + else if (cfgOption(optionId) != NULL) { + if (cfgOptionSource(optionId) != cfgSourceDefault) + strCat(configJson, ","); + + strCat(configJson, "\"value\":"); + switch (cfgDefOptionType(cfgOptionDefIdFromId(optionId))) { case cfgDefOptTypeBoolean: + case cfgDefOptTypeFloat: + case cfgDefOptTypeInteger: { - strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId)); + strCat(configJson, strPtr(varStrForce(cfgOption(optionId)))); break; } - case cfgDefOptTypeFloat: - case cfgDefOptTypeInteger: case cfgDefOptTypeString: { - strCatFmt(mainCallParam, ", '--%s', '%s'", cfgOptionName(optionId), strPtr(varStrForce(cfgOption(optionId)))); + strCatFmt(configJson, "\"%s\"", strPtr(cfgOptionStr(optionId))); break; } @@ -78,14 +102,20 @@ StringList *perlCommand() const KeyValue *valueKv = cfgOptionKv(optionId); const VariantList *keyList = kvKeyList(valueKv); + strCat(configJson, "{"); + for (unsigned int listIdx = 0; listIdx < varLstSize(keyList); listIdx++) { - strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId)); + if (listIdx != 0) + strCat(configJson, ","); + strCatFmt( - mainCallParam, ", '%s=%s'", strPtr(varStr(varLstGet(keyList, listIdx))), + configJson, "\"%s\":\"%s\"", strPtr(varStr(varLstGet(keyList, listIdx))), strPtr(varStr(kvGet(valueKv, varLstGet(keyList, listIdx))))); } + strCat(configJson, "}"); + break; } @@ -93,29 +123,33 @@ StringList *perlCommand() { StringList *valueList = strLstNewVarLst(cfgOptionLst(optionId)); + strCat(configJson, "{"); + for (unsigned int listIdx = 0; listIdx < strLstSize(valueList); listIdx++) { - strCatFmt(mainCallParam, ", '--%s'", cfgOptionName(optionId)); - strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(valueList, listIdx))); + if (listIdx != 0) + strCat(configJson, ","); + + strCatFmt(configJson, "\"%s\":true", strPtr(strLstGet(valueList, listIdx))); } + strCat(configJson, "}"); + break; } } } + + strCat(configJson, "}"); } - // Add help command if it was set - if (cfgCommandHelp()) - strCatFmt(mainCallParam, ", '%s'", cfgCommandName(cfgCmdHelp)); - - // Add command to pass to main - if (cfgCommand() != cfgCmdNone && cfgCommand() != cfgCmdHelp) - strCatFmt(mainCallParam, ", '%s'", cfgCommandName(cfgCommand())); + strCat(configJson, "}"); // Add command arguments to pass to main + String *commandParam = strNew(""); + for (unsigned int paramIdx = 0; paramIdx < strLstSize(cfgCommandParam()); paramIdx++) - strCatFmt(mainCallParam, ", '%s'", strPtr(strLstGet(cfgCommandParam(), paramIdx))); + strCatFmt(commandParam, ",'%s'", strPtr(strLstGet(cfgCommandParam(), paramIdx))); // Add Perl options StringList *perlOptionList = strLstNewVarLst(cfgOptionLst(cfgOptPerlOption)); @@ -125,7 +159,9 @@ StringList *perlCommand() strLstAdd(perlArgList, strLstGet(perlOptionList, argIdx)); // Construct Perl main call - String *mainCall = strNewFmt(PGBACKREST_MAIN "('%s'%s)", strPtr(cfgExe()), strPtr(mainCallParam)); + String *mainCall = strNewFmt( + PGBACKREST_MAIN "('%s','%s','%s'%s)", strPtr(cfgExe()), cfgCommandName(cfgCommand()), strPtr(configJson), + strPtr(commandParam)); // End arg list for perl exec strLstAdd(perlArgList, strNew("-M" PGBACKREST_MODULE)); diff --git a/test/expect/mock-all-001.log b/test/expect/mock-all-001.log index 33c47c6c2..f7357ecdf 100644 --- a/test/expect/mock-all-001.log +++ b/test/expect/mock-all-001.log @@ -80,6 +80,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 16384, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -449,6 +451,8 @@ stop all stanzas (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --force stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/db-master/pgbackrest.conf --force --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --repo-path=[TEST_PATH]/db-master/repo +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 INFO: sent term signal to process [PROCESS-ID] P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] @@ -471,6 +475,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -563,6 +569,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --repo-path=[TEST_PATH]/db-master/repo --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] P00 DEBUG: Protocol::Helper::protocolDestroy(): bComplete = true, iRemoteIdx = [undef], strRemoteType = [undef] @@ -575,6 +583,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --repo-path=[TEST_PATH]/db-master/repo --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 WARN: stop file already exists for stanza db P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] @@ -651,6 +661,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -1009,6 +1021,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = restore P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -1645,6 +1659,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -1964,6 +1980,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -4611,7 +4629,7 @@ info bogus stanza - bogus stanza (db-master host) diff backup - config file warning on local (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --no-online --log-level-console=info 2>&1 --type=diff --stanza=db backup ------------------------------------------------------------------------------------------------------------------------------------ -WARN: [TEST_PATH]/db-master/pgbackrest.conf file contains invalid option 'bogus' +WARN: '[TEST_PATH]/db-master/pgbackrest.conf' contains invalid option 'bogus' P00 INFO: backup command begin [BACKREST-VERSION]: --compress --compress-level=3 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base-2/base --db-timeout=45 --hardlink --lock-path=[TEST_PATH]/db-master/lock --log-level-console=info --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --no-online --protocol-timeout=60 --repo-path=[TEST_PATH]/db-master/repo --stanza=db --start-fast --type=diff P00 WARN: option retention-full is not set, the repository may run out of space HINT: to retain full backups indefinitely (without warning), set option 'retention-full' to the maximum. diff --git a/test/expect/mock-all-002.log b/test/expect/mock-all-002.log index f4be956ac..a134b2666 100644 --- a/test/expect/mock-all-002.log +++ b/test/expect/mock-all-002.log @@ -80,6 +80,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 16384, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -470,6 +472,8 @@ stop all stanzas (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --force stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN] --backup-config=[TEST_PATH]/backup/pgbackrest.conf --backup-host=backup --backup-user=[USER-2] --config=[TEST_PATH]/db-master/pgbackrest.conf --force --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 INFO: sent term signal to process [PROCESS-ID] P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] @@ -492,6 +496,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -598,6 +604,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -645,6 +653,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN] --backup-config=[TEST_PATH]/backup/pgbackrest.conf --backup-host=backup --backup-user=[USER-2] --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] P00 DEBUG: Protocol::Helper::protocolDestroy(): bComplete = true, iRemoteIdx = [undef], strRemoteType = [undef] @@ -657,6 +667,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN] --backup-config=[TEST_PATH]/backup/pgbackrest.conf --backup-host=backup --backup-user=[USER-2] --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 WARN: stop file already exists for stanza db P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] @@ -679,6 +691,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -760,6 +774,8 @@ stop all stanzas (backup host) > [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --force stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/backup/pgbackrest.conf --force --lock-path=[TEST_PATH]/backup/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/backup/log --repo-path=[TEST_PATH]/backup/repo +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 INFO: sent term signal to process [PROCESS-ID] P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] @@ -782,6 +798,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -918,6 +936,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -1295,6 +1315,8 @@ P00 INFO: restore command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = restore P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/db-master/log @@ -1721,6 +1743,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log @@ -2070,6 +2094,8 @@ P00 DEBUG: Storage::Local->pathExists=>: bExists = true P00 DEBUG: Common::Lock::lockAcquire(): bFailOnNoLock = , bRemote = , strLockType = backup P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = P00 DEBUG: Common::Lock::lockStopTest=>: bStopExists = false +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Lock::lockAcquire=>: bResult = true P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 0770, strPathExp = [TEST_PATH]/backup/log diff --git a/test/expect/mock-archive-001.log b/test/expect/mock-archive-001.log index e493cc49c..2b6baa063 100644 --- a/test/expect/mock-archive-001.log +++ b/test/expect/mock-archive-001.log @@ -79,6 +79,8 @@ P00 DEBUG: Archive::Common::walInfo(): strWalFile = [TEST_PATH]/db-master/d P00 DEBUG: Archive::Common::walInfo=>: strDbVersion = 9.4, ullDbSysId = 1000000000000000094 P00 DEBUG: Archive::Push::File::archivePushCheck(): strArchiveFile = 000000010000000100000001, strDbVersion = 9.4, strWalFile = [TEST_PATH]/db-master/db/base/pg_xlog/000000010000000100000001, ullDbSysId = 1000000000000000094 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = , strArchiveClusterPath = [TEST_PATH]/db-master/repo/archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = [TEST_PATH]/db-master/repo/archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = true P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = true @@ -114,6 +116,7 @@ P00 DEBUG: Common::Exit::exitSafe=>: iExitCode = 0 > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get --log-level-console=debug 000000010000000100000001 [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: archive-get command begin [BACKREST-VERSION]: --no-compress --compress-level=3 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base --db-timeout=45 --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --protocol-timeout=60 --repo-cipher-pass= --repo-cipher-type=aes-256-cbc --repo-path=[TEST_PATH]/db-master/repo --stanza=db +P00 DEBUG: Archive::Get::Get->process(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000001 P00 INFO: get WAL segment 000000010000000100000001 P00 DEBUG: Archive::Get::Get->get(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000001 P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = @@ -127,6 +130,8 @@ P00 DEBUG: Db::dbObjectGet=>: iDbMasterIdx = 1, iDbStandbyIdx = [undef], oD P00 DEBUG: Db->info(): strDbPath = <[TEST_PATH]/db-master/db/base> P00 DEBUG: Db->info=>: iDbCatalogVersion = 201409291, iDbControlVersion = 942, strDbVersion = 9.4, ullDbSysId = 1000000000000000094 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = true, strArchiveClusterPath = [TEST_PATH]/db-master/repo/archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = [TEST_PATH]/db-master/repo/archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = true P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = true diff --git a/test/expect/mock-archive-002.log b/test/expect/mock-archive-002.log index 47a54879c..db2a54525 100644 --- a/test/expect/mock-archive-002.log +++ b/test/expect/mock-archive-002.log @@ -100,6 +100,7 @@ P00 DEBUG: Common::Exit::exitSafe=>: iExitCode = 0 > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get --log-level-console=debug 000000010000000100000001 [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: archive-get command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN] --backup-config=[TEST_PATH]/backup/pgbackrest.conf --backup-host=backup --backup-user=[USER-1] --no-compress --compress-level=3 --compress-level-network=1 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base --db-timeout=45 --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --protocol-timeout=60 --stanza=db +P00 DEBUG: Archive::Get::Get->process(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000001 P00 INFO: get WAL segment 000000010000000100000001 P00 DEBUG: Archive::Get::Get->get(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000001 P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = diff --git a/test/expect/mock-stanza-001.log b/test/expect/mock-stanza-001.log index 93fd3edc9..76b8050b8 100644 --- a/test/expect/mock-stanza-001.log +++ b/test/expect/mock-stanza-001.log @@ -189,6 +189,8 @@ P00 DEBUG: Archive::Common::walInfo(): strWalFile = [TEST_PATH]/db-master/d P00 DEBUG: Archive::Common::walInfo=>: strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Push::File::archivePushCheck(): strArchiveFile = 000000010000000100000001, strDbVersion = 9.3, strWalFile = [TEST_PATH]/db-master/db/base/pg_xlog/000000010000000100000001, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = , strArchiveClusterPath = [TEST_PATH]/db-master/repo/archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = [TEST_PATH]/db-master/repo/archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -476,6 +478,8 @@ P00 DEBUG: Archive::Common::walInfo(): strWalFile = [TEST_PATH]/db-master/d P00 DEBUG: Archive::Common::walInfo=>: strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Push::File::archivePushCheck(): strArchiveFile = 000000010000000100000002, strDbVersion = 9.3, strWalFile = [TEST_PATH]/db-master/db/base/pg_xlog/000000010000000100000002, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = , strArchiveClusterPath = [TEST_PATH]/db-master/repo/archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = [TEST_PATH]/db-master/repo/archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -557,6 +561,7 @@ db-version="9.4" > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get 000000010000000100000002 [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: archive-get command begin [BACKREST-VERSION]: --compress-level=3 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base --db-timeout=45 --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --protocol-timeout=60 --repo-path=[TEST_PATH]/db-master/repo --stanza=db +P00 DEBUG: Archive::Get::Get->process(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 INFO: get WAL segment 000000010000000100000002 P00 DEBUG: Archive::Get::Get->get(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = @@ -570,6 +575,8 @@ P00 DEBUG: Db::dbObjectGet=>: iDbMasterIdx = 1, iDbStandbyIdx = [undef], oD P00 DEBUG: Db->info(): strDbPath = <[TEST_PATH]/db-master/db/base> P00 DEBUG: Db->info=>: iDbCatalogVersion = 201306121, iDbControlVersion = 937, strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = true, strArchiveClusterPath = [TEST_PATH]/db-master/repo/archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = [TEST_PATH]/db-master/repo/archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -996,6 +1003,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --repo-path=[TEST_PATH]/db-master/repo --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] P00 DEBUG: Protocol::Helper::protocolDestroy(): bComplete = true, iRemoteIdx = [undef], strRemoteType = [undef] diff --git a/test/expect/mock-stanza-002.log b/test/expect/mock-stanza-002.log index 44c5fb6f4..b5d314a11 100644 --- a/test/expect/mock-stanza-002.log +++ b/test/expect/mock-stanza-002.log @@ -414,6 +414,7 @@ db-version="9.4" > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get 000000010000000100000002 [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: archive-get command begin [BACKREST-VERSION]: --backup-cmd=[BACKREST-BIN] --backup-config=[TEST_PATH]/backup/pgbackrest.conf --backup-host=backup --backup-user=[USER-2] --compress-level=3 --compress-level-network=1 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base --db-timeout=45 --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --protocol-timeout=60 --stanza=db +P00 DEBUG: Archive::Get::Get->process(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 INFO: get WAL segment 000000010000000100000002 P00 DEBUG: Archive::Get::Get->get(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = @@ -821,6 +822,8 @@ stop db stanza (backup host) > [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/backup/pgbackrest.conf --db1-cmd=[BACKREST-BIN] --db1-config=[TEST_PATH]/db-master/pgbackrest.conf --db1-host=db-master --lock-path=[TEST_PATH]/backup/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/backup/log --repo-cipher-pass= --repo-cipher-type=aes-256-cbc --repo-path=[TEST_PATH]/backup/repo --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/backup/lock P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] P00 DEBUG: Protocol::Helper::protocolDestroy(): bComplete = true, iRemoteIdx = [undef], strRemoteType = [undef] diff --git a/test/expect/mock-stanza-003.log b/test/expect/mock-stanza-003.log index 1944fe2ef..ad52829db 100644 --- a/test/expect/mock-stanza-003.log +++ b/test/expect/mock-stanza-003.log @@ -189,6 +189,8 @@ P00 DEBUG: Archive::Common::walInfo(): strWalFile = [TEST_PATH]/db-master/d P00 DEBUG: Archive::Common::walInfo=>: strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Push::File::archivePushCheck(): strArchiveFile = 000000010000000100000001, strDbVersion = 9.3, strWalFile = [TEST_PATH]/db-master/db/base/pg_xlog/000000010000000100000001, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = , strArchiveClusterPath = /archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = /archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -451,6 +453,8 @@ P00 DEBUG: Archive::Common::walInfo(): strWalFile = [TEST_PATH]/db-master/d P00 DEBUG: Archive::Common::walInfo=>: strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Push::File::archivePushCheck(): strArchiveFile = 000000010000000100000002, strDbVersion = 9.3, strWalFile = [TEST_PATH]/db-master/db/base/pg_xlog/000000010000000100000002, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = , strArchiveClusterPath = /archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = /archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -532,6 +536,7 @@ db-version="9.4" > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get 000000010000000100000002 [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: archive-get command begin [BACKREST-VERSION]: --compress-level=3 --config=[TEST_PATH]/db-master/pgbackrest.conf --db1-path=[TEST_PATH]/db-master/db/base --db-timeout=45 --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --protocol-timeout=60 --repo-path=/ --repo-s3-bucket=pgbackrest-dev --repo-s3-endpoint=s3.amazonaws.com --repo-s3-key= --repo-s3-key-secret= --repo-s3-region=us-east-1 --no-repo-s3-verify-ssl --repo-type=s3 --stanza=db +P00 DEBUG: Archive::Get::Get->process(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 INFO: get WAL segment 000000010000000100000002 P00 DEBUG: Archive::Get::Get->get(): strDestinationFile = [TEST_PATH]/db-master/db/base/pg_xlog/RECOVERYXLOG, strSourceArchive = 000000010000000100000002 P00 DEBUG: Common::Lock::lockStopTest(): bStanzaStopRequired = @@ -545,6 +550,8 @@ P00 DEBUG: Db::dbObjectGet=>: iDbMasterIdx = 1, iDbStandbyIdx = [undef], oD P00 DEBUG: Db->info(): strDbPath = <[TEST_PATH]/db-master/db/base> P00 DEBUG: Db->info=>: iDbCatalogVersion = 201306121, iDbControlVersion = 937, strDbVersion = 9.3, ullDbSysId = 1000000000000000093 P00 DEBUG: Archive::Info->new(): bIgnoreMissing = , bLoad = , bRequired = true, strArchiveClusterPath = /archive/db, strCipherPassSub = [undef] +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = 4194304, oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->encrypted(): bIgnoreMissing = true, strFileName = /archive/db/archive.info P00 DEBUG: Storage::Local->encrypted=>: bEncrypted = false P00 DEBUG: Storage::Local->encryptionValid(): bEncrypted = false @@ -983,6 +990,8 @@ stop db stanza (db-master host) > [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db stop ------------------------------------------------------------------------------------------------------------------------------------ P00 INFO: stop command begin [BACKREST-VERSION]: --config=[TEST_PATH]/db-master/pgbackrest.conf --lock-path=[TEST_PATH]/db-master/lock --log-level-console=debug --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --repo-path=/ --repo-s3-bucket=pgbackrest-dev --repo-s3-endpoint=s3.amazonaws.com --repo-s3-key= --repo-s3-key-secret= --repo-s3-region=us-east-1 --no-repo-s3-verify-ssl --repo-type=s3 --stanza=db +P00 DEBUG: Storage::Posix::Driver->new(): bFileSync = , bPathSync = +P00 DEBUG: Storage::Local->new(): bAllowTemp = , hRule = [undef], lBufferMax = [undef], oDriver = [object], strCipherPassUser = [undef], strCipherType = [undef], strDefaultFileMode = <0640>, strDefaultPathMode = <0750>, strPathBase = /, strTempExtension = pgbackrest.tmp P00 DEBUG: Storage::Local->pathCreate(): bCreateParent = true, bIgnoreExists = true, strMode = 770, strPathExp = [TEST_PATH]/db-master/lock P00 DEBUG: Common::Exit::exitSafe(): iExitCode = 0, oException = [undef], strSignal = [undef] P00 DEBUG: Protocol::Helper::protocolDestroy(): bComplete = true, iRemoteIdx = [undef], strRemoteType = [undef] diff --git a/test/lib/pgBackRestTest/Common/DefineTest.pm b/test/lib/pgBackRestTest/Common/DefineTest.pm index 87d0708f4..2463c738c 100644 --- a/test/lib/pgBackRestTest/Common/DefineTest.pm +++ b/test/lib/pgBackRestTest/Common/DefineTest.pm @@ -470,18 +470,6 @@ my $oTestDef = 'config/load' => TESTDEF_COVERAGE_FULL, }, }, - { - &TESTDEF_NAME => 'unit', - &TESTDEF_TOTAL => 1, - }, - { - &TESTDEF_NAME => 'option', - &TESTDEF_TOTAL => 34, - }, - { - &TESTDEF_NAME => 'config-perl', - &TESTDEF_TOTAL => 25, - } ] }, # Storage tests diff --git a/test/lib/pgBackRestTest/Common/JobTest.pm b/test/lib/pgBackRestTest/Common/JobTest.pm index 23f96a253..995fda6b8 100644 --- a/test/lib/pgBackRestTest/Common/JobTest.pm +++ b/test/lib/pgBackRestTest/Common/JobTest.pm @@ -153,7 +153,7 @@ sub run # Create gcov directory my $bGCovExists = true; - if (!$self->{oStorageTest}->pathExists($self->{strGCovPath})) + if ($self->{oTest}->{&TEST_C} && !$self->{oStorageTest}->pathExists($self->{strGCovPath})) { $self->{oStorageTest}->pathCreate($self->{strGCovPath}, {strMode => '0770'}); $bGCovExists = false; @@ -165,7 +165,7 @@ sub run 'docker run -itd -h ' . $self->{oTest}->{&TEST_VM} . "-test --name=${strImage}" . " -v $self->{strCoveragePath}:$self->{strCoveragePath} " . " -v ${strHostTestPath}:${strVmTestPath}" . - " -v $self->{strGCovPath}:$self->{strGCovPath}" . + ($self->{oTest}->{&TEST_C} ? " -v $self->{strGCovPath}:$self->{strGCovPath}" : '') . " -v $self->{strBackRestBase}:$self->{strBackRestBase} " . containerRepo() . ':' . $self->{oTest}->{&TEST_VM} . "-test", diff --git a/test/lib/pgBackRestTest/Env/ConfigEnvTest.pm b/test/lib/pgBackRestTest/Env/ConfigEnvTest.pm index e184300c4..729384bd2 100644 --- a/test/lib/pgBackRestTest/Env/ConfigEnvTest.pm +++ b/test/lib/pgBackRestTest/Env/ConfigEnvTest.pm @@ -12,10 +12,12 @@ use warnings FATAL => qw(all); use Carp qw(confess); use English '-no_match_vars'; +use Getopt::Long qw(GetOptions); + use pgBackRest::Common::Exception; use pgBackRest::Common::Log; use pgBackRest::Config::Config; -use pgBackRestBuild::Config::Data; +use pgBackRest::Version; use constant CONFIGENVTEST => 'ConfigEnvTest'; @@ -101,13 +103,347 @@ sub commandTestWrite return @szyParam; } +#################################################################################################################################### +# testOptionValidate +# +# Make sure the command-line options are valid based on the command. +#################################################################################################################################### +sub testOptionValidate +{ + my $iCommandId = shift; + + # Build hash with all valid command-line options + my @stryOptionAllow; + + for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++) + { + my $strOption = cfgOptionName($iOptionId); + + if (cfgDefOptionType($iOptionId) == CFGDEF_TYPE_HASH || cfgDefOptionType($iOptionId) == CFGDEF_TYPE_LIST) + { + $strOption .= '=s@'; + } + elsif (cfgDefOptionType($iOptionId) != CFGDEF_TYPE_BOOLEAN) + { + $strOption .= '=s'; + } + + push(@stryOptionAllow, $strOption); + + # Check if the option can be negated + if (cfgDefOptionNegate($iOptionId)) + { + push(@stryOptionAllow, 'no-' . cfgOptionName($iOptionId)); + } + } + + # Get command-line options + my $oOptionTest = {}; + + # Parse command line options + if (!GetOptions($oOptionTest, @stryOptionAllow)) + { + confess &log(ASSERT, "error parsing command line"); + } + + # Store results of validation + my $rhOption = {}; + + # Keep track of unresolved dependencies + my $bDependUnresolved = true; + my %oOptionResolved; + + # Loop through all possible options + while ($bDependUnresolved) + { + # Assume that all dependencies will be resolved in this loop + $bDependUnresolved = false; + + for (my $iOptionId = 0; $iOptionId < cfgOptionTotal(); $iOptionId++) + { + my $strOption = cfgOptionName($iOptionId); + + # Skip the option if it has been resolved in a prior loop + if (defined($oOptionResolved{$strOption})) + { + next; + } + + # Determine if an option is valid for a command + $rhOption->{$strOption}{valid} = cfgDefOptionValid($iCommandId, $iOptionId); + + if (!$rhOption->{$strOption}{valid}) + { + $oOptionResolved{$strOption} = true; + next; + } + + # Store the option value + my $strValue = $oOptionTest->{$strOption}; + + # Check to see if an option can be negated. Make sure that it is not set and negated at the same time. + $rhOption->{$strOption}{negate} = false; + + if (cfgDefOptionNegate($iOptionId)) + { + $rhOption->{$strOption}{negate} = defined($$oOptionTest{'no-' . $strOption}); + + if ($rhOption->{$strOption}{negate} && defined($strValue)) + { + confess &log(ERROR, "option '${strOption}' cannot be both set and negated", ERROR_OPTION_NEGATE); + } + + if ($rhOption->{$strOption}{negate} && cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_BOOLEAN) + { + $strValue = false; + } + } + + # Check dependency for the command then for the option + my $bDependResolved = true; + my $strDependOption; + my $strDependValue; + my $strDependType; + + if (cfgDefOptionDepend($iCommandId, $iOptionId)) + { + # Check if the depend option has a value + my $iDependOptionId = cfgDefOptionDependOption($iCommandId, $iOptionId); + $strDependOption = cfgOptionName($iDependOptionId); + $strDependValue = $rhOption->{$strDependOption}{value}; + + # Make sure the depend option has been resolved, otherwise skip this option for now + if (!defined($oOptionResolved{$strDependOption})) + { + $bDependUnresolved = true; + next; + } + + if (!defined($strDependValue)) + { + $bDependResolved = false; + $strDependType = 'source'; + } + + # If a depend value exists, make sure the option value matches + if ($bDependResolved && cfgDefOptionDependValueTotal($iCommandId, $iOptionId) == 1 && + cfgDefOptionDependValue($iCommandId, $iOptionId, 0) ne $strDependValue) + { + $bDependResolved = false; + $strDependType = 'value'; + } + + # If a depend list exists, make sure the value is in the list + if ($bDependResolved && cfgDefOptionDependValueTotal($iCommandId, $iOptionId) > 1 && + !cfgDefOptionDependValueValid($iCommandId, $iOptionId, $strDependValue)) + { + $bDependResolved = false; + $strDependType = 'list'; + } + } + + if (cfgDefOptionDepend($iCommandId, $iOptionId) && !$bDependResolved && defined($strValue)) + { + my $strError = "option '${strOption}' not valid without option "; + my $iDependOptionId = cfgOptionId($strDependOption); + + if ($strDependType eq 'source') + { + confess &log(ERROR, "${strError}'${strDependOption}'", ERROR_OPTION_INVALID); + } + + # If a depend value exists, make sure the option value matches + if ($strDependType eq 'value') + { + if (cfgDefOptionType($iDependOptionId) eq CFGDEF_TYPE_BOOLEAN) + { + $strError .= + "'" . (cfgDefOptionDependValue($iCommandId, $iOptionId, 0) ? '' : 'no-') . "${strDependOption}'"; + } + else + { + $strError .= "'${strDependOption}' = '" . cfgDefOptionDependValue($iCommandId, $iOptionId, 0) . "'"; + } + + confess &log(ERROR, $strError, ERROR_OPTION_INVALID); + } + + $strError .= "'${strDependOption}'"; + + # If a depend list exists, make sure the value is in the list + if ($strDependType eq 'list') + { + my @oyValue; + + for (my $iValueId = 0; $iValueId < cfgDefOptionDependValueTotal($iCommandId, $iOptionId); $iValueId++) + { + push(@oyValue, "'" . cfgDefOptionDependValue($iCommandId, $iOptionId, $iValueId) . "'"); + } + + $strError .= @oyValue == 1 ? " = $oyValue[0]" : " in (" . join(", ", @oyValue) . ")"; + confess &log(ERROR, $strError, ERROR_OPTION_INVALID); + } + } + + # Is the option defined? + if (defined($strValue)) + { + # Check that floats and integers are valid + if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_INTEGER || + cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_FLOAT) + { + # Test that the string is a valid float or integer by adding 1 to it. It's pretty hokey but it works and it + # beats requiring Scalar::Util::Numeric to do it properly. + my $bError = false; + + eval + { + my $strTest = $strValue + 1; + return true; + } + or do + { + $bError = true; + }; + + # Check that integers are really integers + if (!$bError && cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_INTEGER && + (int($strValue) . 'S') ne ($strValue . 'S')) + { + $bError = true; + } + + # Error if the value did not pass tests + !$bError + or confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE); + } + + # Process an allow list for the command then for the option + if (cfgDefOptionAllowList($iCommandId, $iOptionId) && + !cfgDefOptionAllowListValueValid($iCommandId, $iOptionId, $strValue)) + { + confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_VALUE); + } + + # Process an allow range for the command then for the option + if (cfgDefOptionAllowRange($iCommandId, $iOptionId) && + ($strValue < cfgDefOptionAllowRangeMin($iCommandId, $iOptionId) || + $strValue > cfgDefOptionAllowRangeMax($iCommandId, $iOptionId))) + { + confess &log(ERROR, "'${strValue}' is not valid for '${strOption}' option", ERROR_OPTION_INVALID_RANGE); + } + + # Set option value + if (ref($strValue) eq 'ARRAY' && + (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH || cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_LIST)) + { + foreach my $strItem (@{$strValue}) + { + my $strKey; + my $strValue; + + # If the keys are expected to have values + if (cfgDefOptionType($iOptionId) eq CFGDEF_TYPE_HASH) + { + # Check for = and make sure there is a least one character on each side + my $iEqualPos = index($strItem, '='); + + if ($iEqualPos < 1 || length($strItem) <= $iEqualPos + 1) + { + confess &log(ERROR, "'${strItem}' not valid key/value for '${strOption}' option", + ERROR_OPTION_INVALID_PAIR); + } + + $strKey = substr($strItem, 0, $iEqualPos); + $strValue = substr($strItem, $iEqualPos + 1); + } + # Else no values are expected so set value to true + else + { + $strKey = $strItem; + $strValue = true; + } + + # Check that the key has not already been set + if (defined($rhOption->{$strOption}{$strKey}{value})) + { + confess &log(ERROR, "'${$strItem}' already defined for '${strOption}' option", + ERROR_OPTION_DUPLICATE_KEY); + } + + # Set key/value + $rhOption->{$strOption}{value}{$strKey} = $strValue; + } + } + else + { + $rhOption->{$strOption}{value} = $strValue; + } + + # If not config sourced then it must be a param + if (!defined($rhOption->{$strOption}{source})) + { + $rhOption->{$strOption}{source} = CFGDEF_SOURCE_PARAM; + } + } + # Else try to set a default + elsif ($bDependResolved) + { + # Source is default for this option + $rhOption->{$strOption}{source} = CFGDEF_SOURCE_DEFAULT; + + # Check for default in command then option + my $strDefault = cfgDefOptionDefault($iCommandId, $iOptionId); + + # If default is defined + if (defined($strDefault)) + { + # Only set default if dependency is resolved + $rhOption->{$strOption}{value} = $strDefault if !$rhOption->{$strOption}{negate}; + } + # Else check required + elsif (cfgDefOptionRequired($iCommandId, $iOptionId)) + { + confess &log(ERROR, + cfgCommandName($iCommandId) . " command requires option: ${strOption}" . + ERROR_OPTION_REQUIRED); + } + } + + $oOptionResolved{$strOption} = true; + } + } + + # Make sure all options specified on the command line are valid + foreach my $strOption (sort(keys(%{$oOptionTest}))) + { + # Strip "no-" off the option + $strOption = $strOption =~ /^no\-/ ? substr($strOption, 3) : $strOption; + + if (!$rhOption->{$strOption}{valid}) + { + confess &log( + ERROR, "option '${strOption}' not valid for command '" . cfgCommandName($iCommandId) . "'", ERROR_OPTION_COMMAND); + } + } + + return $rhOption; +} + +#################################################################################################################################### +# Load the configuration +#################################################################################################################################### sub configTestLoad { my $self = shift; my $iCommandId = shift; @ARGV = $self->commandTestWrite(cfgCommandName($iCommandId), $self->{&CONFIGENVTEST}); - $self->testResult(sub {configLoad(false)}, true, 'config load: ' . join(" ", @ARGV)); + $self->testResult( + sub {configLoad( + false, backrestBin(), cfgCommandName($iCommandId), + (JSON::PP->new()->allow_nonref())->encode(testOptionValidate($iCommandId)))}, + true, 'config load: ' . join(" ", @ARGV)); } #################################################################################################################################### diff --git a/test/lib/pgBackRestTest/Module/Config/ConfigConfigPerlTest.pm b/test/lib/pgBackRestTest/Module/Config/ConfigConfigPerlTest.pm deleted file mode 100644 index 1efc2bae5..000000000 --- a/test/lib/pgBackRestTest/Module/Config/ConfigConfigPerlTest.pm +++ /dev/null @@ -1,362 +0,0 @@ -#################################################################################################################################### -# ConfigConfigTest.pm - Tests for mixed command line and config file options in Config.pm -#################################################################################################################################### -package pgBackRestTest::Module::Config::ConfigConfigPerlTest; -use parent 'pgBackRestTest::Env::ConfigEnvTest'; - -#################################################################################################################################### -# Perl includes -#################################################################################################################################### -use strict; -use warnings FATAL => qw(all); -use Carp qw(confess); - -use pgBackRest::Common::Exception; -use pgBackRest::Common::Ini; -use pgBackRest::Common::Log; -use pgBackRest::Config::Config; - -use pgBackRestTest::Common::RunTest; - -#################################################################################################################################### -# run -#################################################################################################################################### -sub run -{ - my $self = shift; - - my $oConfig = {}; - my $strConfigFile = $self->testPath() . '/pgbackrest.conf'; - - if ($self->begin('set and negate option ' . cfgOptionName(CFGOPT_CONFIG))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, '/dude/dude.conf'); - $self->optionTestSetBool(CFGOPT_CONFIG, false); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_NEGATE, cfgOptionName(CFGOPT_CONFIG)); - } - - if ($self->begin('option ' . cfgOptionName(CFGOPT_CONFIG))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_CONFIG, false); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_CONFIG); - } - - if ($self->begin('default option ' . cfgOptionName(CFGOPT_CONFIG))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_CONFIG, cfgDefOptionDefault(CFGCMD_BACKUP, CFGOPT_CONFIG)); - } - - if ($self->begin('config file is a path')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $self->testPath()); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_FILE_INVALID, $self->testPath()); - } - - if ($self->begin('load from config stanza command section - option ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $oConfig = {}; - $$oConfig{$self->stanza() . ':' . cfgCommandName(CFGCMD_BACKUP)}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 2; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 2); - } - - if ($self->begin('load from config stanza section - option ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $oConfig = {}; - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 3; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 3); - } - - if ($self->begin('load from config global command section - option thread-max')) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_BACKUP)}{'thread-max'} = 2; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 2); - } - - if ($self->begin('load from config global section - option ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 5; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 5); - } - - if ($self->begin('default - option ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $oConfig = {}; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 1); - } - - if ($self->begin('command-line override - option ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 9; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_PROCESS_MAX, 7); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 7); - } - - if ($self->begin('invalid boolean - option ' . cfgOptionName(CFGOPT_HARDLINK))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_BACKUP)}{cfgOptionName(CFGOPT_HARDLINK)} = 'Y'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, 'Y', cfgOptionName(CFGOPT_HARDLINK)); - } - - if ($self->begin('invalid value - option ' . cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE)} = BOGUS; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE)); - } - - if ($self->begin('valid value - option ' . cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE)} = lc(INFO); - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - } - - if ($self->begin('archive-push - option ' . cfgOptionName(CFGOPT_LOG_LEVEL_CONSOLE))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_ARCHIVE_PUSH)); - } - - if ($self->begin(cfgCommandName(CFGCMD_EXPIRE) . ' ' . cfgOptionName(CFGOPT_RETENTION_FULL))) - { - $oConfig = {}; - $$oConfig{$self->stanza() . ':' . cfgCommandName(CFGCMD_EXPIRE)}{cfgOptionName(CFGOPT_RETENTION_FULL)} = 2; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_EXPIRE)); - $self->optionTestExpect(CFGOPT_RETENTION_FULL, 2); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' option ' . cfgOptionName(CFGOPT_COMPRESS))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_BACKUP)}{cfgOptionName(CFGOPT_COMPRESS)} = 'n'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_COMPRESS, false); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' global option ' . cfgOptionName(CFGOPT_RECOVERY_OPTION) . ' error')) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_RESTORE)}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = 'bogus='; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_VALUE, 'bogus=', cfgOptionName(CFGOPT_RECOVERY_OPTION)); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' global option ' . cfgOptionName(CFGOPT_RECOVERY_OPTION) . ' error')) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_RESTORE)}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = '=bogus'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_VALUE, '=bogus', cfgOptionName(CFGOPT_RECOVERY_OPTION)); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' global option ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_RESTORE)}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = - 'archive-command=/path/to/pgbackrest'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - $self->optionTestExpect(CFGOPT_RECOVERY_OPTION, '/path/to/pgbackrest', 'archive-command'); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' stanza option ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $oConfig = {}; - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_RECOVERY_OPTION)} = ['standby-mode=on', 'a=b']; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - $self->optionTestExpect(CFGOPT_RECOVERY_OPTION, 'b', 'a'); - $self->optionTestExpect(CFGOPT_RECOVERY_OPTION, 'on', 'standby-mode'); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' option ' . cfgOptionName(CFGOPT_DB_PATH))) - { - $oConfig = {}; - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_DB_PATH)} = '/path/to/db'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_DB_PATH, '/path/to/db'); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' option ' . cfgOptionName(CFGOPT_ARCHIVE_CHECK))) - { - $oConfig = {}; - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_DB_PATH)} = '/path/to/db'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - $self->optionTestSetBool(CFGOPT_ARCHIVE_CHECK, false); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_ONLINE, true); - $self->optionTestExpect(CFGOPT_ARCHIVE_CHECK, false); - } - - if ($self->begin(cfgCommandName(CFGCMD_ARCHIVE_PUSH) . ' option ' . cfgOptionName(CFGOPT_DB_PATH))) - { - $oConfig = {}; - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_DB_PATH)} = '/path/to/db'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_ARCHIVE_PUSH)); - $self->optionTestExpect(CFGOPT_DB_PATH, '/path/to/db'); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' option ' . cfgOptionName(CFGOPT_REPO_PATH))) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_REPO_PATH)} = '/repo'; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_REPO_PATH, '/repo'); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' option ' . cfgOptionName(CFGOPT_REPO_PATH) . ' multiple times')) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_REPO_PATH)} = ['/repo', '/repo2']; - storageTest()->put($strConfigFile, iniRender($oConfig, true)); - - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_CONFIG, $strConfigFile); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_MULTIPLE_VALUE, cfgOptionName(CFGOPT_REPO_PATH)); - } -} - -#################################################################################################################################### -# Getters -#################################################################################################################################### -# Change this from the default so the same stanza is not used in all tests. -sub stanza {return 'main'}; - -1; diff --git a/test/lib/pgBackRestTest/Module/Config/ConfigOptionTest.pm b/test/lib/pgBackRestTest/Module/Config/ConfigOptionTest.pm deleted file mode 100644 index 3f2487d48..000000000 --- a/test/lib/pgBackRestTest/Module/Config/ConfigOptionTest.pm +++ /dev/null @@ -1,404 +0,0 @@ -#################################################################################################################################### -# ConfigOptionTest.pm - Tests for command line options in Config.pm -#################################################################################################################################### -package pgBackRestTest::Module::Config::ConfigOptionTest; -use parent 'pgBackRestTest::Env::ConfigEnvTest'; - -#################################################################################################################################### -# Perl includes -#################################################################################################################################### -use strict; -use warnings FATAL => qw(all); -use Carp qw(confess); -use English '-no_match_vars'; - -use Cwd qw(abs_path); - -use pgBackRest::Common::Exception; -use pgBackRest::Common::Log; -use pgBackRest::Config::Config; -use pgBackRest::Version; - -use pgBackRestTest::Common::RunTest; - -#################################################################################################################################### -# run -#################################################################################################################################### -sub run -{ - my $self = shift; - - my @oyArray; - - if ($self->begin('backup with no stanza')) - { - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_REQUIRED, cfgOptionName(CFGOPT_STANZA)); - } - - if ($self->begin('backup with boolean stanza')) - { - $self->optionTestSetBool(CFGOPT_STANZA); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_COMMAND_REQUIRED); - } - - if ($self->begin('backup type defaults to ' . CFGOPTVAL_BACKUP_TYPE_INCR)) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid option ' . cfgOptionName(CFGOPT_ARCHIVE_ASYNC))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_ARCHIVE_ASYNC); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_COMMAND, cfgOptionName(CFGOPT_ARCHIVE_ASYNC), - cfgCommandName(CFGCMD_BACKUP)); - } - - if ($self->begin('backup type set to ' . CFGOPTVAL_BACKUP_TYPE_FULL)) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL); - } - - if ($self->begin('backup type invalid')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TYPE, BOGUS); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_TYPE)); - } - - if ($self->begin('backup invalid force')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_FORCE); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_FORCE), 'no-' . cfgOptionName(CFGOPT_ONLINE)); - } - - if ($self->begin('backup valid force')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_ONLINE, false); - $self->optionTestSetBool(CFGOPT_FORCE); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_ONLINE, false); - $self->optionTestExpect(CFGOPT_FORCE, true); - } - - if ($self->begin('backup invalid value for ' . cfgOptionName(CFGOPT_TEST_DELAY))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_TEST); - $self->optionTestSet(CFGOPT_TEST_DELAY, BOGUS); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_TEST_DELAY)); - } - - if ($self->begin('backup invalid ' . cfgOptionName(CFGOPT_TEST_DELAY))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TEST_DELAY, 5); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_TEST_DELAY), cfgOptionName(CFGOPT_TEST)); - } - - if ($self->begin('backup check ' . cfgOptionName(CFGOPT_TEST_DELAY) . ' undef')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_TEST_DELAY); - } - - if ($self->begin('restore invalid ' . cfgOptionName(CFGOPT_TARGET))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TYPE, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_TYPE)); - $self->optionTestSet(CFGOPT_TARGET, BOGUS); - - @oyArray = (CFGOPTVAL_RESTORE_TYPE_NAME, CFGOPTVAL_RESTORE_TYPE_TIME, CFGOPTVAL_RESTORE_TYPE_XID); - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID, cfgOptionName(CFGOPT_TARGET), - cfgOptionName(CFGOPT_TYPE), \@oyArray); - } - - if ($self->begin('restore ' . cfgOptionName(CFGOPT_TARGET))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_NAME); - $self->optionTestSet(CFGOPT_TARGET, BOGUS); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - $self->optionTestExpect(CFGOPT_TYPE, CFGOPTVAL_RESTORE_TYPE_NAME); - $self->optionTestExpect(CFGOPT_TARGET, BOGUS); - $self->optionTestExpect(CFGOPT_TARGET_TIMELINE); - } - - if ($self->begin('invalid string ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_PROCESS_MAX, BOGUS); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_PROCESS_MAX)); - } - - if ($self->begin('invalid float ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_PROCESS_MAX, '0.0'); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, '0.0', cfgOptionName(CFGOPT_PROCESS_MAX)); - } - - if ($self->begin('valid ' . cfgOptionName(CFGOPT_PROCESS_MAX))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_PROCESS_MAX, '2'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - } - - if ($self->begin('valid thread-max')) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetByName('thread-max', '2'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROCESS_MAX, 2); - } - - if ($self->begin('valid float ' . cfgOptionName(CFGOPT_TEST_DELAY))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_TEST); - $self->optionTestSet(CFGOPT_TEST_DELAY, '0.25'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - } - - if ($self->begin('valid int ' . cfgOptionName(CFGOPT_TEST_DELAY))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSetBool(CFGOPT_TEST); - $self->optionTestSet(CFGOPT_TEST_DELAY, 3); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - } - - if ($self->begin('restore valid ' . cfgOptionName(CFGOPT_TARGET_TIMELINE))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_TARGET_TIMELINE, 2); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - } - - if ($self->begin('invalid ' . cfgOptionName(CFGOPT_COMPRESS_LEVEL))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_COMPRESS_LEVEL, '12'); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_RANGE, '12', cfgOptionName(CFGOPT_COMPRESS_LEVEL)); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, BOGUS); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, BOGUS, cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE)); - } - - if ($self->begin( - cfgCommandName(CFGCMD_BACKUP) . ' default value ' . cfgDefOptionDefault(CFGCMD_BACKUP, CFGOPT_RETENTION_ARCHIVE_TYPE) . - ' for ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' with valid ' . cfgOptionName(CFGOPT_RETENTION_FULL))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RETENTION_FULL, 1); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, 1); - $self->optionTestExpect(CFGOPT_RETENTION_FULL, 1); - $self->optionTestExpect(CFGOPT_RETENTION_DIFF, undef); - # Default is FULL so this test will fail if the default is changed, alerting to the need to update configLoad. - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_FULL); - } - - if ($self->begin( - cfgCommandName(CFGCMD_BACKUP) . ' valid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE) . ' for ' . - cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' ' . CFGOPTVAL_BACKUP_TYPE_DIFF)) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RETENTION_DIFF, 1); - $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_DIFF); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, 1); - $self->optionTestExpect(CFGOPT_RETENTION_DIFF, 1); - $self->optionTestExpect(CFGOPT_RETENTION_FULL, undef); - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_DIFF); - } - - if ($self->begin( - cfgCommandName(CFGCMD_BACKUP) . ' warn no valid value ' . cfgOptionName(CFGOPT_RETENTION_ARCHIVE) . ' for ' . - cfgOptionName(CFGOPT_RETENTION_ARCHIVE_TYPE) . ' ' . CFGOPTVAL_BACKUP_TYPE_INCR)) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RETENTION_FULL, 2); - $self->optionTestSet(CFGOPT_RETENTION_DIFF, 1); - $self->optionTestSet(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE, undef); - $self->optionTestExpect(CFGOPT_RETENTION_ARCHIVE_TYPE, CFGOPTVAL_BACKUP_TYPE_INCR); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' invalid value ' . cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_DB_TIMEOUT, 5); - $self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 4); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_INVALID_VALUE, 4, cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT), - "'protocol-timeout' option (4) should be greater than 'db-timeout' option (5)"); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RECOVERY_OPTION, '='); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, '=', cfgOptionName(CFGOPT_RECOVERY_OPTION)); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RECOVERY_OPTION, '=' . BOGUS); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, '=' . BOGUS, cfgOptionName(CFGOPT_RECOVERY_OPTION)); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' invalid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RECOVERY_OPTION, BOGUS . '='); - - $self->configTestLoadExpect( - cfgCommandName(CFGCMD_RESTORE), ERROR_OPTION_INVALID_PAIR, BOGUS . '=', cfgOptionName(CFGOPT_RECOVERY_OPTION)); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' valid value ' . cfgOptionName(CFGOPT_RECOVERY_OPTION))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_RECOVERY_OPTION, 'primary-conn-info=db.domain.net'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - $self->optionTestExpect(&CFGOPT_RECOVERY_OPTION, 'db.domain.net', 'primary-conn-info'); - } - - if ($self->begin(cfgCommandName(CFGCMD_RESTORE) . ' values passed to ' . cfgCommandName(CFGCMD_ARCHIVE_GET))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db path/main'); - $self->optionTestSet(CFGOPT_REPO_PATH, '/repo'); - $self->optionTestSet(CFGOPT_BACKUP_HOST, 'db.mydomain.com'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_RESTORE)); - - my $strCommand = cfgCommandWrite(CFGCMD_ARCHIVE_GET); - my $strExpectedCommand = - backrestBin() . " --backup-host=db.mydomain.com \"--db1-path=/db path/main\" --repo-path=/repo --stanza=app " . - cfgCommandName(CFGCMD_ARCHIVE_GET); - - if ($strCommand ne $strExpectedCommand) - { - confess "expected command '${strExpectedCommand}' but got '${strCommand}'"; - } - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' default value ' . cfgOptionName(CFGOPT_DB_CMD))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_HOST, 'db'); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_DB_CMD, backrestBin()); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' missing option ' . cfgOptionName(CFGOPT_DB_PATH))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP), ERROR_OPTION_REQUIRED, 'db1-path', 'does this stanza exist?'); - } - - if ($self->begin(cfgCommandName(CFGCMD_BACKUP) . ' automatically increase ' . cfgOptionName(CFGOPT_PROTOCOL_TIMEOUT))) - { - $self->optionTestSet(CFGOPT_STANZA, $self->stanza()); - $self->optionTestSet(CFGOPT_DB_PATH, '/db'); - $self->optionTestSet(CFGOPT_DB_TIMEOUT, cfgDefOptionDefault(CFGCMD_BACKUP, CFGOPT_PROTOCOL_TIMEOUT) + 30); - - $self->configTestLoadExpect(cfgCommandName(CFGCMD_BACKUP)); - $self->optionTestExpect(CFGOPT_PROTOCOL_TIMEOUT, cfgDefOptionDefault(CFGCMD_BACKUP, CFGOPT_PROTOCOL_TIMEOUT) + 60); - } -} - -#################################################################################################################################### -# Getters -#################################################################################################################################### -# Change this from the default so the same stanza is not used in all tests. -sub stanza {return 'app'}; - -1; diff --git a/test/lib/pgBackRestTest/Module/Config/ConfigUnitTest.pm b/test/lib/pgBackRestTest/Module/Config/ConfigUnitTest.pm deleted file mode 100644 index f3675b53c..000000000 --- a/test/lib/pgBackRestTest/Module/Config/ConfigUnitTest.pm +++ /dev/null @@ -1,113 +0,0 @@ -#################################################################################################################################### -# ConfigUnitTest.pm - Tests code paths -#################################################################################################################################### -package pgBackRestTest::Module::Config::ConfigUnitTest; -use parent 'pgBackRestTest::Env::ConfigEnvTest'; - -#################################################################################################################################### -# Perl includes -#################################################################################################################################### -use strict; -use warnings FATAL => qw(all); -use Carp qw(confess); - -use pgBackRest::Common::Exception; -use pgBackRest::Common::Ini; -use pgBackRest::Common::Log; -use pgBackRest::Config::Config; - -use pgBackRestTest::Common::RunTest; - -#################################################################################################################################### -# run -#################################################################################################################################### -sub run -{ - my $self = shift; - - my $oConfig = {}; - my $strConfigFile = $self->testPath() . '/pgbackrest.conf'; - cfgCommandSet(CFGCMD_ARCHIVE_GET); - cfgOptionSet(CFGOPT_CONFIG, $strConfigFile, true); - - if ($self->begin('Config::configFileValidate()')) - { - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_DB_PORT)} = 1234; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, false, - 'valid option ' . cfgOptionName(CFGOPT_DB_PORT) . ' under invalid section', - {strLogExpect => - "WARN: $strConfigFile valid option '" . cfgOptionName(CFGOPT_DB_PORT) . "' is a stanza section option and is not" . - " valid in section " . CFGDEF_SECTION_GLOBAL . "\n" . - "HINT: global options can be specified in global or stanza sections but not visa-versa"}); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_BACKUP)}{cfgOptionName(CFGOPT_DB_PORT)} = 1234; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, false, - 'valid option ' . cfgOptionName(CFGOPT_DB_PORT) . ' for command ' . cfgCommandName(CFGCMD_BACKUP) . - ' under invalid global section', - {strLogExpect => - "WARN: $strConfigFile valid option '" . cfgOptionName(CFGOPT_DB_PORT) . "' is a stanza section option and is not" . - " valid in section " . CFGDEF_SECTION_GLOBAL . "\n" . - "HINT: global options can be specified in global or stanza sections but not visa-versa"}); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{$self->stanza() . ':' . cfgCommandName(CFGCMD_ARCHIVE_PUSH)}{cfgOptionName(CFGOPT_DB_PORT)} = 1234; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, false, - 'valid option ' . cfgOptionName(CFGOPT_DB_PORT) . ' under invalid stanza section command', - {strLogExpect => - "WARN: $strConfigFile valid option '" . cfgOptionName(CFGOPT_DB_PORT) . "' is not valid for command '" . - cfgCommandName(CFGCMD_ARCHIVE_PUSH) ."'"}); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{&BOGUS} = BOGUS; - - $self->testResult( - sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, false, - 'invalid option ' . $$oConfig{&CFGDEF_SECTION_GLOBAL}{&BOGUS}, - {strLogExpect => "WARN: $strConfigFile file contains invalid option '" . BOGUS . "'"}); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{'thread-max'} = 3; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, true, 'valid alt name found'); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_LOG_LEVEL_STDERR)} = - cfgDefOptionDefault(CFGCMD_ARCHIVE_PUSH, CFGOPT_LOG_LEVEL_STDERR); - $$oConfig{$self->stanza()}{cfgOptionName(CFGOPT_DB_PATH)} = '/db'; - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_ARCHIVE_PUSH)}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 2; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, true, 'valid config file'); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{cfgOptionName(CFGOPT_LOG_LEVEL_STDERR)} = - cfgDefOptionDefault(CFGCMD_ARCHIVE_PUSH, CFGOPT_LOG_LEVEL_STDERR); - $$oConfig{&CFGDEF_SECTION_GLOBAL . ':' . cfgCommandName(CFGCMD_ARCHIVE_PUSH)}{cfgOptionName(CFGOPT_PROCESS_MAX)} = 2; - $$oConfig{'unusual-section^name!:' . cfgCommandName(CFGCMD_CHECK)}{cfgOptionName(CFGOPT_DB_PATH)} = '/db'; - - $self->testResult(sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, true, 'valid unusual section name'); - - #--------------------------------------------------------------------------------------------------------------------------- - $oConfig = {}; - $$oConfig{&CFGDEF_SECTION_GLOBAL}{&BOGUS} = BOGUS; - - # Change command to indicate remote - cfgCommandSet(CFGCMD_REMOTE); - - $self->testResult( - sub {pgBackRest::Config::Config::configFileValidate($oConfig)}, true, - 'invalid option but config file not validated on remote'); - } -} - -1; diff --git a/test/src/common/logTest.c b/test/src/common/logTest.c index 9895dd22d..91ed499b9 100644 --- a/test/src/common/logTest.c +++ b/test/src/common/logTest.c @@ -14,6 +14,7 @@ Log Test Harness Name of file where logs are stored for testing ***********************************************************************************************************************************/ String *stdoutFile = NULL; +String *stderrFile = NULL; /*********************************************************************************************************************************** Initialize log for testing @@ -25,6 +26,9 @@ testLogInit() stdoutFile = strNewFmt("%s/stdout.log", testPath()); logHandleStdOut = open(strPtr(stdoutFile), O_WRONLY | O_CREAT | O_TRUNC, 0640); + + stderrFile = strNewFmt("%s/stderr.log", testPath()); + logHandleStdErr = open(strPtr(stderrFile), O_WRONLY | O_CREAT | O_TRUNC, 0640); } /*********************************************************************************************************************************** @@ -44,6 +48,23 @@ testLogResult(const char *expected) logHandleStdOut = open(strPtr(stdoutFile), O_WRONLY | O_CREAT | O_TRUNC, 0640); } +/*********************************************************************************************************************************** +Compare error log to a static string + +After the comparison the log is cleared so the next result can be compared. +***********************************************************************************************************************************/ +void +testLogErrResult(const char *expected) +{ + String *actual = strTrim(strNewBuf(storageGet(storageLocal(), stderrFile, false))); + + if (!strEqZ(actual, expected)) + THROW(AssertError, "\n\nexpected error log:\n\n%s\n\nbut actual error log was:\n\n%s\n\n", expected, strPtr(actual)); + + close(logHandleStdErr); + logHandleStdErr = open(strPtr(stderrFile), O_WRONLY | O_CREAT | O_TRUNC, 0640); +} + /*********************************************************************************************************************************** Make sure nothing is left in the log after all tests have completed ***********************************************************************************************************************************/ @@ -54,4 +75,9 @@ testLogFinal() if (!strEqZ(actual, "")) THROW(AssertError, "\n\nexpected log to be empty but actual log was:\n\n%s\n\n", strPtr(actual)); + + actual = strTrim(strNewBuf(storageGet(storageLocal(), stderrFile, false))); + + if (!strEqZ(actual, "")) + THROW(AssertError, "\n\nexpected error log to be empty but actual error log was:\n\n%s\n\n", strPtr(actual)); } diff --git a/test/src/common/logTest.h b/test/src/common/logTest.h index fcf2792c4..440dbb9ba 100644 --- a/test/src/common/logTest.h +++ b/test/src/common/logTest.h @@ -9,6 +9,7 @@ Functions ***********************************************************************************************************************************/ void testLogInit(); void testLogResult(const char *expected); +void testLogErrResult(const char *expected); void testLogFinal(); #endif diff --git a/test/src/module/archive/pushTest.c b/test/src/module/archive/pushTest.c index 02ed5294d..58a345590 100644 --- a/test/src/module/archive/pushTest.c +++ b/test/src/module/archive/pushTest.c @@ -3,7 +3,7 @@ Test Archive Push Command ***********************************************************************************************************************************/ #include -#include "config/parse.h" +#include "config/load.h" /*********************************************************************************************************************************** Test Run @@ -20,7 +20,8 @@ void testRun() strLstAddZ(argList, "--archive-timeout=1"); strLstAddZ(argList, "--stanza=db"); strLstAddZ(argList, "archive-push"); - configParse(strLstSize(argList), strLstPtr(argList)); + cfgLoad(strLstSize(argList), strLstPtr(argList)); + logInit(logLevelInfo, logLevelOff, false); // ------------------------------------------------------------------------------------------------------------------------- String *segment = strNew("000000010000000100000001"); @@ -90,13 +91,13 @@ void testRun() strLstAddZ(argList, "--archive-timeout=1"); strLstAddZ(argList, "--stanza=db"); strLstAddZ(argList, "archive-push"); - configParse(strLstSize(argList), strLstPtr(argList)); + cfgLoad(strLstSize(argList), strLstPtr(argList)); TEST_ERROR(cmdArchivePush(), ParamRequiredError, "WAL segment to push required"); // ------------------------------------------------------------------------------------------------------------------------- strLstAddZ(argList, "000000010000000100000001"); - configParse(strLstSize(argList), strLstPtr(argList)); + cfgLoad(strLstSize(argList), strLstPtr(argList)); TEST_ERROR(cmdArchivePush(), AssertError, "archive-push in C does not support synchronous mode"); @@ -107,7 +108,8 @@ void testRun() strLstAdd(argList, strNewFmt("--perl-bin=%s", strPtr(perlBin))); strLstAdd(argList, strNewFmt("--spool-path=%s", testPath())); strLstAddZ(argList, "--archive-async"); - configParse(strLstSize(argList), strLstPtr(argList)); + cfgLoad(strLstSize(argList), strLstPtr(argList)); + logInit(logLevelInfo, logLevelOff, false); TRY_BEGIN() { diff --git a/test/src/module/common/logTest.c b/test/src/module/common/logTest.c index d5f7be37c..cc3a31808 100644 --- a/test/src/module/common/logTest.c +++ b/test/src/module/common/logTest.c @@ -30,7 +30,7 @@ void testRun() if (testBegin("logInit()")) { TEST_RESULT_INT(logLevelStdOut, logLevelOff, "console logging is off"); - TEST_RESULT_INT(logLevelStdErr, logLevelWarn, "stderr logging is warn"); + TEST_RESULT_INT(logLevelStdErr, logLevelOff, "stderr logging is off"); TEST_RESULT_VOID(logInit(logLevelInfo, logLevelError, true), "init logging"); TEST_RESULT_INT(logLevelStdOut, logLevelInfo, "console logging is info"); TEST_RESULT_INT(logLevelStdErr, logLevelError, "stderr logging is error"); diff --git a/test/src/module/config/parseTest.c b/test/src/module/config/parseTest.c index 417293d36..60b6ad527 100644 --- a/test/src/module/config/parseTest.c +++ b/test/src/module/config/parseTest.c @@ -335,6 +335,15 @@ void testRun() ))); TEST_RESULT_VOID(configParse(strLstSize(argList), strLstPtr(argList)), TEST_COMMAND_BACKUP " command"); + logInit(logLevelInfo, logLevelOff, false); + testLogErrResult( + strPtr( + strNewFmt( + "WARN: '%s' contains option 'recovery-option' invalid for section 'db:backup'\n" + "WARN: '%s' contains invalid option 'bogus'\n" + "WARN: '%s' contains negate option 'no-compress'\n" + "WARN: '%s' contains command-line only option 'online'", + strPtr(configFile), strPtr(configFile), strPtr(configFile), strPtr(configFile)))); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptDbPath)), "/path/to/db", " db-path is set"); TEST_RESULT_INT(cfgOptionSource(cfgOptDbPath), cfgSourceConfig, " db-path is source config"); diff --git a/test/src/module/perl/execTest.c b/test/src/module/perl/execTest.c index 3e29abf05..1922544a7 100644 --- a/test/src/module/perl/execTest.c +++ b/test/src/module/perl/execTest.c @@ -25,13 +25,13 @@ void testRun() TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - "/usr/bin/perl|" TEST_PERL_MAIN "', 'info')|[NULL]", "custom command with no options"); + "/usr/bin/perl|" TEST_PERL_MAIN "','info','{}')|[NULL]", "custom command with no options"); cfgOptionSet(cfgOptPerlBin, cfgSourceParam, NULL); TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "', 'info')|[NULL]", "command with no options"); + TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "','info','{}')|[NULL]", "command with no options"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); @@ -49,16 +49,21 @@ void testRun() cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1)); cfgOptionValidSet(cfgOptCompressLevel, true); - cfgOptionSet(cfgOptCompressLevel, cfgSourceParam, varNewInt(3)); + cfgOptionSet(cfgOptCompressLevel, cfgSourceConfig, varNewInt(3)); cfgOptionValidSet(cfgOptStanza, true); - cfgOptionSet(cfgOptStanza, cfgSourceParam, varNewStr(strNew("db"))); + cfgOptionSet(cfgOptStanza, cfgSourceDefault, varNewStr(strNew("db"))); TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "'" - ", '--compress', '--compress-level', '3', '--no-online', '--protocol-timeout', '1.1', '--stanza', 'db'" - ", 'backup')|[NULL]", "simple options"); + TEST_ENV_EXE "|" TEST_PERL_EXE "|" TEST_PERL_MAIN "','backup','{" + "\"compress\":{\"source\":\"param\",\"value\":true}," + "\"compress-level\":{\"source\":\"config\",\"value\":3}," + "\"online\":{\"source\":\"param\",\"negate\":true}," + "\"protocol-timeout\":{\"source\":\"param\",\"value\":1.1}," + "\"stanza\":{\"value\":\"db\"}" + "}')|[NULL]", + "simple options"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); @@ -94,11 +99,11 @@ void testRun() TEST_RESULT_STR( strPtr(strLstJoin(perlCommand(), "|")), - TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|-MDevel::Cover=-silent,1|" TEST_PERL_MAIN "'" - ", '--db-include', 'db1', '--db-include', 'db2'" - ", '--perl-option', '-I.', '--perl-option', '-MDevel::Cover=-silent,1'" - ", '--recovery-option', 'standby_mode=on', '--recovery-option', 'primary_conn_info=blah'" - ", 'help', 'restore', 'param1', 'param2')|[NULL]", "complex options"); + TEST_ENV_EXE "|" TEST_PERL_EXE "|-I.|-MDevel::Cover=-silent,1|" TEST_PERL_MAIN "','restore','{" + "\"db-include\":{\"source\":\"param\",\"value\":{\"db1\":true,\"db2\":true}}," + "\"perl-option\":{\"source\":\"param\",\"value\":{\"-I.\":true,\"-MDevel::Cover=-silent,1\":true}}," + "\"recovery-option\":{\"source\":\"param\",\"value\":{\"standby_mode\":\"on\",\"primary_conn_info\":\"blah\"}}" + "}','param1','param2')|[NULL]", "complex options"); } // ----------------------------------------------------------------------------------------------------------------------------- diff --git a/test/src/module/storage/storageTest.c b/test/src/module/storage/storageTest.c index da291f32f..6fd8b8e51 100644 --- a/test/src/module/storage/storageTest.c +++ b/test/src/module/storage/storageTest.c @@ -86,7 +86,8 @@ void testRun() TEST_RESULT_PTR(storageList(storage, strNew(BOGUS_STR), NULL, true), NULL, "ignore missing dir"); TEST_RESULT_VOID(storagePut(storage, strNew("aaa.txt"), bufNewStr(strNew("aaa"))), "write aaa.text"); - TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, NULL, false), ", ")), "aaa.txt, stdout.log", "dir list"); + TEST_RESULT_STR( + strPtr(strLstJoin(storageList(storage, NULL, NULL, false), ", ")), "aaa.txt, stderr.log, stdout.log", "dir list"); TEST_RESULT_VOID(storagePut(storage, strNew("bbb.txt"), bufNewStr(strNew("bbb"))), "write bbb.text"); TEST_RESULT_STR(strPtr(strLstJoin(storageList(storage, NULL, strNew("^bbb"), false), ", ")), "bbb.txt", "dir list");