mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-06 03:53:59 +02:00
Add beta feature infrastructure.
This allows options to be marked as beta, which will require that the --beta option be supplied to prevent accidental usage of a beta feature. The online and command-line documentation also show warnings when options are beta.
This commit is contained in:
parent
6b409d049e
commit
24f725212d
@ -19,6 +19,7 @@ use pgBackRestDoc::ProjectInfo;
|
||||
####################################################################################################################################
|
||||
# Help types
|
||||
####################################################################################################################################
|
||||
use constant CONFIG_HELP_BETA => 'beta';
|
||||
use constant CONFIG_HELP_COMMAND => 'command';
|
||||
push @EXPORT, qw(CONFIG_HELP_COMMAND);
|
||||
use constant CONFIG_HELP_CURRENT => 'current';
|
||||
@ -319,6 +320,7 @@ sub process
|
||||
$$oCommandOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
|
||||
$oCommandOption->{&CONFIG_HELP_INTERNAL} =
|
||||
cfgDefineCommand()->{$strCommand}{&CFGDEF_INTERNAL} ? true : $oOptionDefine->{$strOption}{&CFGDEF_INTERNAL};
|
||||
$oCommandOption->{&CONFIG_HELP_BETA} = $oOptionDefine->{$strOption}{&CFGDEF_BETA};
|
||||
|
||||
# If internal is defined for the option/command it overrides everthing else
|
||||
if (defined($oOptionDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_INTERNAL}))
|
||||
@ -370,6 +372,7 @@ sub process
|
||||
$$oOption{&CONFIG_HELP_DESCRIPTION} = $$oCommandOption{&CONFIG_HELP_DESCRIPTION};
|
||||
$$oOption{&CONFIG_HELP_EXAMPLE} = $oOptionDoc->fieldGet('example');
|
||||
$oOption->{&CONFIG_HELP_INTERNAL} = $oOptionDefine->{$strOption}{&CFGDEF_INTERNAL};
|
||||
$oOption->{&CONFIG_HELP_BETA} = $oOptionDefine->{$strOption}{&CFGDEF_BETA};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,6 +822,12 @@ sub helpOptionGet
|
||||
$oOptionElement->
|
||||
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_SUMMARY});
|
||||
|
||||
# Add beta warning
|
||||
if ($$oOptionHash{&CONFIG_HELP_BETA})
|
||||
{
|
||||
$oOptionElement->nodeAdd('p')->textSet({name => 'text', children => ['FOR BETA TESTING ONLY. DO NOT USE IN PRODUCTION.']});
|
||||
}
|
||||
|
||||
$oOptionElement->
|
||||
nodeAdd('p')->textSet($$oOptionHash{&CONFIG_HELP_DESCRIPTION});
|
||||
|
||||
@ -862,32 +871,38 @@ sub helpOptionGet
|
||||
}
|
||||
|
||||
# Get the example
|
||||
my $strExample;
|
||||
my $strExample = '';
|
||||
|
||||
my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_GROUP};
|
||||
my $strOptionIndex = defined($strOptionPrefix) ?
|
||||
"${strOptionPrefix}1-" . substr($strOption, length($strOptionPrefix) + 1) : $strOption;
|
||||
|
||||
if (defined($strCommand))
|
||||
if (defined($strCommand) && docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
|
||||
{
|
||||
if (docConfigOptionTypeTest($strOption, CFGDEF_TYPE_BOOLEAN))
|
||||
if ($$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'n' && $$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'y')
|
||||
{
|
||||
if ($$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'n' && $$oOptionHash{&CONFIG_HELP_EXAMPLE} ne 'y')
|
||||
{
|
||||
confess &log(ERROR, "option ${strOption} example should be boolean but value is: " .
|
||||
$$oOptionHash{&CONFIG_HELP_EXAMPLE});
|
||||
}
|
||||
confess &log(ERROR, "option ${strOption} example should be boolean but value is: " .
|
||||
$$oOptionHash{&CONFIG_HELP_EXAMPLE});
|
||||
}
|
||||
|
||||
$strExample = '--' . ($$oOptionHash{&CONFIG_HELP_EXAMPLE} eq 'n' ? 'no-' : '') . $strOptionIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
$strExample = "--${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
|
||||
}
|
||||
$strExample = '--' . ($$oOptionHash{&CONFIG_HELP_EXAMPLE} eq 'n' ? 'no-' : '') . $strOptionIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
$strExample = "${strOptionIndex}=" . $$oOptionHash{&CONFIG_HELP_EXAMPLE};
|
||||
foreach my $strLine (split('\\|', $$oOptionHash{&CONFIG_HELP_EXAMPLE}))
|
||||
{
|
||||
if ($strExample ne '')
|
||||
{
|
||||
$strExample .= ' ';
|
||||
}
|
||||
|
||||
if (defined($strCommand))
|
||||
{
|
||||
$strExample .= '--';
|
||||
}
|
||||
|
||||
$strExample .= "${strOptionIndex}=${strLine}";
|
||||
}
|
||||
}
|
||||
|
||||
$strCodeBlock .= (defined($strCodeBlock) ? "\n" : '') . "example: ${strExample}";
|
||||
|
@ -147,6 +147,8 @@ use constant CFGDEF_DEFAULT_LITERAL => 'default-
|
||||
use constant CFGDEF_GROUP => 'group';
|
||||
push @EXPORT, qw(CFGDEF_GROUP);
|
||||
|
||||
use constant CFGDEF_BETA => 'beta';
|
||||
push @EXPORT, qw(CFGDEF_BETA);
|
||||
use constant CFGDEF_INDEX => 'index';
|
||||
push @EXPORT, qw(CFGDEF_INDEX);
|
||||
use constant CFGDEF_INHERIT => 'inherit';
|
||||
|
@ -610,6 +610,13 @@ option:
|
||||
command-role:
|
||||
main: {}
|
||||
|
||||
beta:
|
||||
section: global
|
||||
type: boolean
|
||||
default: false
|
||||
internal: true
|
||||
beta: true
|
||||
|
||||
buffer-size:
|
||||
section: global
|
||||
type: size
|
||||
|
@ -42,6 +42,7 @@ STRING_EXTERN(OPT_TYPE_TIME_STR, OPT_TYPE_TIM
|
||||
/***********************************************************************************************************************************
|
||||
Option constants
|
||||
***********************************************************************************************************************************/
|
||||
STRING_EXTERN(OPT_BETA_STR, OPT_BETA);
|
||||
STRING_EXTERN(OPT_STANZA_STR, OPT_STANZA);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -297,6 +298,7 @@ typedef struct BldCfgOptionRaw
|
||||
const String *type;
|
||||
const String *section;
|
||||
bool internal;
|
||||
bool beta;
|
||||
const Variant *required;
|
||||
const Variant *negate;
|
||||
bool reset;
|
||||
@ -815,6 +817,10 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
{
|
||||
optRaw.internal = yamlBoolParse(optDefVal);
|
||||
}
|
||||
else if (strEqZ(optDef.value, "beta"))
|
||||
{
|
||||
optRaw.beta = yamlBoolParse(optDefVal);
|
||||
}
|
||||
else if (strEqZ(optDef.value, "negate"))
|
||||
{
|
||||
optRaw.negate = varNewBool(yamlBoolParse(optDefVal));
|
||||
@ -904,6 +910,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
.type = strDup(optRaw->type),
|
||||
.section = strDup(optRaw->section),
|
||||
.internal = optRaw->internal,
|
||||
.beta = optRaw->beta,
|
||||
.required = varBool(optRaw->required),
|
||||
.negate = varBool(optRaw->negate),
|
||||
.reset = optRaw->reset,
|
||||
|
@ -51,6 +51,8 @@ STRING_DECLARE(OPT_TYPE_TIME_STR);
|
||||
/***********************************************************************************************************************************
|
||||
Option constants
|
||||
***********************************************************************************************************************************/
|
||||
#define OPT_BETA "beta"
|
||||
STRING_DECLARE(OPT_BETA_STR);
|
||||
#define OPT_STANZA "stanza"
|
||||
STRING_DECLARE(OPT_STANZA_STR);
|
||||
|
||||
@ -118,6 +120,7 @@ struct BldCfgOption
|
||||
const String *type; // Option type, e.g. integer
|
||||
const String *section; // Option section, i.e. stanza or global
|
||||
bool internal; // Is the option internal?
|
||||
bool beta; // Is the option beta?
|
||||
bool required; // Is the option required?
|
||||
bool negate; // Can the option be negated?
|
||||
bool reset; // Can the option be reset?
|
||||
|
@ -711,6 +711,9 @@ bldCfgRenderParseAutoC(const Storage *const storageRepo, const BldCfg bldCfg, co
|
||||
" PARSE_RULE_OPTION_TYPE(%s),\n",
|
||||
strZ(opt->name), strZ(bldEnum("cfgOptType", opt->type)));
|
||||
|
||||
if (opt->beta)
|
||||
strCatZ(configOpt, " PARSE_RULE_OPTION_BETA(true),\n");
|
||||
|
||||
if (opt->negate)
|
||||
strCatZ(configOpt, " PARSE_RULE_OPTION_NEGATE(true),\n");
|
||||
|
||||
|
@ -151,6 +151,16 @@
|
||||
</text>
|
||||
|
||||
<config-key-list>
|
||||
<config-key id="beta" name="Allow Beta Features">
|
||||
<summary>Allow beta features.</summary>
|
||||
|
||||
<text>
|
||||
<p>Allow beta features to be enabled but do not actually enable them. Beta features need to be separately enabled using related options.</p>
|
||||
</text>
|
||||
|
||||
<example>2MiB</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="buffer-size" name="Buffer Size">
|
||||
<summary>Buffer size for I/O operations.</summary>
|
||||
|
||||
|
@ -97,11 +97,14 @@ helpRenderSplitSize(const String *string, const char *delimiter, size_t size)
|
||||
Helper function for helpRender() to make output look good on a console
|
||||
***********************************************************************************************************************************/
|
||||
static String *
|
||||
helpRenderText(const String *const text, const bool internal, const size_t indent, const bool indentFirst, const size_t length)
|
||||
helpRenderText(
|
||||
const String *const text, const bool internal, const bool beta, const size_t indent, const bool indentFirst,
|
||||
const size_t length)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STRING, text);
|
||||
FUNCTION_LOG_PARAM(BOOL, internal);
|
||||
FUNCTION_LOG_PARAM(BOOL, beta);
|
||||
FUNCTION_LOG_PARAM(SIZE, indent);
|
||||
FUNCTION_LOG_PARAM(BOOL, indentFirst);
|
||||
FUNCTION_LOG_PARAM(SIZE, length);
|
||||
@ -116,7 +119,12 @@ helpRenderText(const String *const text, const bool internal, const size_t inden
|
||||
{
|
||||
// Split the text into paragraphs
|
||||
const StringList *const lineList = strLstNewSplitZ(
|
||||
strNewFmt("%s%s", strZ(text), internal ? "\n\nFOR INTERNAL USE ONLY. DO NOT USE IN PRODUCTION." : ""), "\n");
|
||||
strNewFmt(
|
||||
"%s%s", strZ(text),
|
||||
beta ?
|
||||
"\n\nFOR BETA TESTING ONLY. DO NOT USE IN PRODUCTION." :
|
||||
(internal ? "\n\nFOR INTERNAL USE ONLY. DO NOT USE IN PRODUCTION." : "")),
|
||||
"\n");
|
||||
|
||||
// Iterate through each paragraph and split the lines according to the line length
|
||||
for (unsigned int lineIdx = 0; lineIdx < strLstSize(lineList); lineIdx++)
|
||||
@ -313,7 +321,7 @@ helpRender(const Buffer *const helpData)
|
||||
strCatFmt(
|
||||
result, " %s%*s%s\n", cfgParseCommandName(commandId),
|
||||
(int)(commandSizeMax - strlen(cfgParseCommandName(commandId)) + 2), "",
|
||||
strZ(helpRenderText(commandData[commandId].summary, false, commandSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
strZ(helpRenderText(commandData[commandId].summary, false, false, commandSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
}
|
||||
|
||||
// Construct message for more help
|
||||
@ -405,10 +413,10 @@ helpRender(const Buffer *const helpData)
|
||||
"%s\n"
|
||||
"\n"
|
||||
"%s\n",
|
||||
strZ(helpRenderText(commandData[commandId].summary, false, 0, true, CONSOLE_WIDTH)),
|
||||
strZ(helpRenderText(commandData[commandId].summary, false, false, 0, true, CONSOLE_WIDTH)),
|
||||
strZ(
|
||||
helpRenderText(
|
||||
commandData[commandId].description, commandData[commandId].internal, 0, true, CONSOLE_WIDTH)));
|
||||
commandData[commandId].description, commandData[commandId].internal, false, 0, true, CONSOLE_WIDTH)));
|
||||
|
||||
// Construct key/value of sections and options
|
||||
KeyValue *optionKv = kvNew();
|
||||
@ -484,7 +492,7 @@ helpRender(const Buffer *const helpData)
|
||||
strCatFmt(
|
||||
result, " --%s%*s%s\n",
|
||||
cfgParseOptionName(optionId), (int)(optionSizeMax - strlen(cfgParseOptionName(optionId)) + 2), "",
|
||||
strZ(helpRenderText(summary, false, optionSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
strZ(helpRenderText(summary, false, false, optionSizeMax + 6, false, CONSOLE_WIDTH)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,7 +515,7 @@ helpRender(const Buffer *const helpData)
|
||||
if (!option.found || !cfgParseOptionValid(cfgCommand(), cfgCmdRoleMain, option.id))
|
||||
THROW_FMT(OptionInvalidError, "option '%s' is not valid for command '%s'", strZ(optionName), commandName);
|
||||
|
||||
// Output option summary and description. Add a warning for internal options.
|
||||
// Output option summary and description. Add a warning for internal and beta options.
|
||||
CHECK(
|
||||
AssertError, optionData[option.id].summary != NULL && optionData[option.id].description != NULL,
|
||||
"option help missing");
|
||||
@ -520,9 +528,11 @@ helpRender(const Buffer *const helpData)
|
||||
"\n"
|
||||
"%s\n",
|
||||
cfgParseOptionName(option.id),
|
||||
strZ(helpRenderText(optionData[option.id].summary, false, 0, true, CONSOLE_WIDTH)),
|
||||
strZ(helpRenderText(optionData[option.id].summary, false, false, 0, true, CONSOLE_WIDTH)),
|
||||
strZ(
|
||||
helpRenderText(optionData[option.id].description, optionData[option.id].internal, 0, true, CONSOLE_WIDTH)));
|
||||
helpRenderText(
|
||||
optionData[option.id].description, optionData[option.id].internal, option.beta, 0, true,
|
||||
CONSOLE_WIDTH)));
|
||||
|
||||
// Output current and default values if they exist
|
||||
const String *defaultValue = cfgOptionDefault(option.id);
|
||||
|
@ -55,6 +55,7 @@ Option constants
|
||||
#define CFGOPT_ARCHIVE_PUSH_QUEUE_MAX "archive-push-queue-max"
|
||||
#define CFGOPT_ARCHIVE_TIMEOUT "archive-timeout"
|
||||
#define CFGOPT_BACKUP_STANDBY "backup-standby"
|
||||
#define CFGOPT_BETA "beta"
|
||||
#define CFGOPT_BUFFER_SIZE "buffer-size"
|
||||
#define CFGOPT_CHECKSUM_PAGE "checksum-page"
|
||||
#define CFGOPT_CIPHER_PASS "cipher-pass"
|
||||
@ -131,7 +132,7 @@ Option constants
|
||||
#define CFGOPT_TYPE "type"
|
||||
#define CFGOPT_VERBOSE "verbose"
|
||||
|
||||
#define CFG_OPTION_TOTAL 164
|
||||
#define CFG_OPTION_TOTAL 165
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option value constants
|
||||
@ -372,6 +373,7 @@ typedef enum
|
||||
cfgOptArchivePushQueueMax,
|
||||
cfgOptArchiveTimeout,
|
||||
cfgOptBackupStandby,
|
||||
cfgOptBeta,
|
||||
cfgOptBufferSize,
|
||||
cfgOptChecksumPage,
|
||||
cfgOptCipherPass,
|
||||
|
@ -1112,6 +1112,88 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
), // opt/backup-standby
|
||||
), // opt/backup-standby
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTION_NAME("beta"), // opt/beta
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeBoolean), // opt/beta
|
||||
PARSE_RULE_OPTION_BETA(true), // opt/beta
|
||||
PARSE_RULE_OPTION_NEGATE(true), // opt/beta
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/beta
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/beta
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/beta
|
||||
// opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdServer) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdServerPing) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStart) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStop) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/beta
|
||||
), // opt/beta
|
||||
// opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/beta
|
||||
), // opt/beta
|
||||
// opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/beta
|
||||
), // opt/beta
|
||||
// opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/beta
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/beta
|
||||
), // opt/beta
|
||||
// opt/beta
|
||||
PARSE_RULE_OPTIONAL // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_OPTIONAL_DEFAULT // opt/beta
|
||||
( // opt/beta
|
||||
PARSE_RULE_VAL_BOOL_FALSE, // opt/beta
|
||||
), // opt/beta
|
||||
), // opt/beta
|
||||
), // opt/beta
|
||||
), // opt/beta
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/buffer-size
|
||||
( // opt/buffer-size
|
||||
PARSE_RULE_OPTION_NAME("buffer-size"), // opt/buffer-size
|
||||
@ -9575,6 +9657,7 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptArchivePushQueueMax, // opt-resolve-order
|
||||
cfgOptArchiveTimeout, // opt-resolve-order
|
||||
cfgOptBackupStandby, // opt-resolve-order
|
||||
cfgOptBeta, // opt-resolve-order
|
||||
cfgOptBufferSize, // opt-resolve-order
|
||||
cfgOptChecksumPage, // opt-resolve-order
|
||||
cfgOptCipherPass, // opt-resolve-order
|
||||
|
@ -125,6 +125,7 @@ typedef struct ParseRuleOption
|
||||
{
|
||||
const char *name; // Name
|
||||
unsigned int type : 4; // e.g. string, int, boolean
|
||||
bool beta : 1; // Is the option a beta feature?
|
||||
bool negate : 1; // Can the option be negated on the command line?
|
||||
bool reset : 1; // Can the option be reset on the command line?
|
||||
bool required : 1; // Is the option required?
|
||||
@ -168,6 +169,9 @@ typedef enum
|
||||
#define PARSE_RULE_OPTION_TYPE(typeParam) \
|
||||
.type = typeParam
|
||||
|
||||
#define PARSE_RULE_OPTION_BETA(betaParam) \
|
||||
.beta = betaParam
|
||||
|
||||
#define PARSE_RULE_OPTION_NEGATE(negateParam) \
|
||||
.negate = negateParam
|
||||
|
||||
@ -343,6 +347,32 @@ parseOptionIdxValue(ParseOption *optionList, unsigned int optionId, unsigned int
|
||||
FUNCTION_TEST_RETURN_TYPE_P(ParseOptionValue, &optionList[optionId].indexList[optionKeyIdx]);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Check that --beta is set if a beta option is used
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
parseOptionBeta(
|
||||
const unsigned int optionId, const unsigned int optionKeyIdx, const bool beta, const ParseOption *const parseOptionBeta)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT, optionId);
|
||||
FUNCTION_TEST_PARAM(UINT, optionKeyIdx);
|
||||
FUNCTION_TEST_PARAM(BOOL, beta);
|
||||
FUNCTION_TEST_PARAM_P(PARSE_OPTION, parseOptionBeta);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
if (beta && optionId != cfgOptBeta && (parseOptionBeta->indexListTotal == 0 || parseOptionBeta->indexList[0].negate))
|
||||
{
|
||||
THROW_FMT(
|
||||
OptionInvalidError,
|
||||
"option '%s' is not valid without option '" CFGOPT_BETA "'\n"
|
||||
"HINT: beta features require the --" CFGOPT_BETA " option to prevent accidental usage.",
|
||||
cfgParseOptionKeyIdxName(optionId, optionKeyIdx));
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get command id by name
|
||||
***********************************************************************************************************************************/
|
||||
@ -681,6 +711,9 @@ cfgParseOption(const String *const optionCandidate, const CfgParseOptionParam pa
|
||||
}
|
||||
}
|
||||
|
||||
// Set the beta flag
|
||||
result.beta = optionFound->beta;
|
||||
|
||||
FUNCTION_TEST_RETURN_TYPE(CfgParseOptionResult, result);
|
||||
}
|
||||
|
||||
@ -2158,6 +2191,9 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
|
||||
configOptionValue->set = true;
|
||||
configOptionValue->source = parseOptionValue->source;
|
||||
|
||||
// Check beta status
|
||||
parseOptionBeta(optionId, optionKeyIdx, parseRuleOption[optionId].beta, &parseOptionList[cfgOptBeta]);
|
||||
|
||||
if (optionType == cfgOptTypeBoolean)
|
||||
{
|
||||
configOptionValue->value.boolean = !parseOptionValue->negate;
|
||||
|
@ -67,6 +67,7 @@ typedef struct CfgParseOptionResult
|
||||
bool negate; // Was the option negated?
|
||||
bool reset; // Was the option reset?
|
||||
bool deprecated; // Is the option deprecated?
|
||||
bool beta; // Is the option in beta?
|
||||
} CfgParseOptionResult;
|
||||
|
||||
#define cfgParseOptionP(optionName, ...) \
|
||||
|
@ -1090,6 +1090,7 @@ sub configCreate
|
||||
|
||||
# General options
|
||||
# ------------------------------------------------------------------------------------------------------------------------------
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'beta'} = 'y';
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'job-retry'} = 0;
|
||||
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'log-level-console'} = lc(DETAIL);
|
||||
|
@ -152,6 +152,7 @@ option:
|
||||
|
||||
# Options that are not used but must be present for modules to compile. All must have a default or not be required.
|
||||
#---------------------------------------------------------------------------------------------------------------------------------
|
||||
beta: {type: boolean, default: false, command: {noop: {}}}
|
||||
compress-level-network: {type: string, required: false, command: {noop: {}}}
|
||||
config: {
|
||||
type: string, internal: true, default: CFGOPTDEF_CONFIG_PATH "/" PROJECT_CONFIG_FILE, default-literal: true, negate: true}
|
||||
|
@ -6,6 +6,7 @@
|
||||
<operation>
|
||||
<operation-general>
|
||||
<option-list>
|
||||
<option id="beta"><summary></summary><text><p></p></text></option>
|
||||
<option id="config"><summary></summary><text><p></p></text></option>
|
||||
<option id="config-path"><summary></summary><text><p></p></text></option>
|
||||
<option id="config-include-path"><summary></summary><text><p></p></text></option>
|
||||
|
@ -42,6 +42,10 @@ hrnCfgLoad(ConfigCommand commandId, const StringList *argListParam, const HrnCfg
|
||||
// Add standard options needed in most cases
|
||||
if (!param.noStd)
|
||||
{
|
||||
// Enable beta features
|
||||
if (cfgParseOptionValid(commandId, param.role, cfgOptBeta))
|
||||
strLstInsert(argList, 0, STRDEF("--" CFGOPT_BETA));
|
||||
|
||||
// Set job retry to 0 if it is valid
|
||||
if (cfgParseOptionValid(commandId, param.role, cfgOptJobRetry))
|
||||
strLstInsert(argList, 0, strNewFmt("--" CFGOPT_JOB_RETRY "=%u", param.jobRetry));
|
||||
|
@ -401,6 +401,7 @@ testRun(void)
|
||||
" backup-standby:\n"
|
||||
" section: global\n"
|
||||
" type: boolean\n"
|
||||
" beta: true\n"
|
||||
" default: false\n"
|
||||
" depend:\n"
|
||||
" option: online\n"
|
||||
@ -759,6 +760,7 @@ testRun(void)
|
||||
" (\n"
|
||||
" PARSE_RULE_OPTION_NAME(\"backup-standby\"),\n"
|
||||
" PARSE_RULE_OPTION_TYPE(cfgOptTypeBoolean),\n"
|
||||
" PARSE_RULE_OPTION_BETA(true),\n"
|
||||
" PARSE_RULE_OPTION_NEGATE(true),\n"
|
||||
" PARSE_RULE_OPTION_RESET(true),\n"
|
||||
" PARSE_RULE_OPTION_REQUIRED(true),\n"
|
||||
|
@ -98,17 +98,24 @@ testRun(void)
|
||||
if (testBegin("helpRenderText()"))
|
||||
{
|
||||
TEST_RESULT_STR_Z(
|
||||
helpRenderText(STRDEF("this is a short sentence"), false, 0, false, 80), "this is a short sentence", "one line");
|
||||
helpRenderText(STRDEF("this is a short sentence"), false, false, 0, false, 80), "this is a short sentence", "one line");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
helpRenderText(STRDEF("this is a short sentence"), false, 4, false, 14),
|
||||
helpRenderText(STRDEF("this is a short sentence"), false, false, 4, false, 14),
|
||||
"this is a\n"
|
||||
" short\n"
|
||||
" sentence",
|
||||
"three lines, no indent first");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
helpRenderText(STRDEF("This is a short paragraph.\n\nHere is another one."), true, 2, true, 16),
|
||||
helpRenderText(STRDEF("this is a short sentence"), true, true, 4, true, 132),
|
||||
" this is a short sentence\n"
|
||||
"\n"
|
||||
" FOR BETA TESTING ONLY. DO NOT USE IN PRODUCTION.",
|
||||
"beta feature");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
helpRenderText(STRDEF("This is a short paragraph.\n\nHere is another one."), true, false, 2, true, 16),
|
||||
" This is a\n"
|
||||
" short\n"
|
||||
" paragraph.\n"
|
||||
|
@ -1634,7 +1634,7 @@ testRun(void)
|
||||
RECOVERY_SETTING_HEADER
|
||||
"a_setting = 'a'\n"
|
||||
"b_setting = 'b'\n"
|
||||
"restore_command = '" TEST_PROJECT_EXE " --lock-path=" HRN_PATH "/lock --log-path=" HRN_PATH " --pg1-path=/pg"
|
||||
"restore_command = '" TEST_PROJECT_EXE " --beta --lock-path=" HRN_PATH "/lock --log-path=" HRN_PATH " --pg1-path=/pg"
|
||||
" --repo1-path=/repo --stanza=test1 archive-get %f \"%p\"'\n",
|
||||
"check recovery options");
|
||||
|
||||
@ -1648,8 +1648,8 @@ testRun(void)
|
||||
TEST_RESULT_STR_Z(
|
||||
restoreRecoveryConf(PG_VERSION_94, restoreLabel),
|
||||
RECOVERY_SETTING_HEADER
|
||||
"restore_command = '/usr/local/bin/pg_wrapper.sh --lock-path=" HRN_PATH "/lock --log-path=" HRN_PATH " --pg1-path=/pg"
|
||||
" --repo1-path=/repo --stanza=test1 archive-get %f \"%p\"'\n",
|
||||
"restore_command = '/usr/local/bin/pg_wrapper.sh --beta --lock-path=" HRN_PATH "/lock --log-path=" HRN_PATH
|
||||
" --pg1-path=/pg --repo1-path=/repo --stanza=test1 archive-get %f \"%p\"'\n",
|
||||
"restore_command invokes /usr/local/bin/pg_wrapper.sh per --cmd option");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -1163,6 +1163,37 @@ testRun(void)
|
||||
"HINT: this option could expose secrets in the process list.\n"
|
||||
"HINT: specify the option in a configuration file or an environment variable instead.");
|
||||
|
||||
// These tests cheat a bit but there may not always be a beta option available for more general testing
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("beta option dependency");
|
||||
|
||||
// Need to parse a command so there will be a valid index for the error message
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, TEST_BACKREST_EXE);
|
||||
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/db");
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
|
||||
strLstAddZ(argList, TEST_COMMAND_BACKUP);
|
||||
|
||||
ParseOption betaOption = {0};
|
||||
|
||||
TEST_ERROR(
|
||||
parseOptionBeta(cfgOptRepoPath, 0, true, &betaOption), OptionInvalidError,
|
||||
"option 'repo1-path' is not valid without option 'beta'\n"
|
||||
"HINT: beta features require the --beta option to prevent accidental usage.");
|
||||
|
||||
betaOption.indexListTotal = 1;
|
||||
ParseOptionValue betaOptionValue = {.negate = true};
|
||||
betaOption.indexList = &betaOptionValue;
|
||||
|
||||
TEST_ERROR(
|
||||
parseOptionBeta(cfgOptRepoPath, 0, true, &betaOption), OptionInvalidError,
|
||||
"option 'repo1-path' is not valid without option 'beta'\n"
|
||||
"HINT: beta features require the --beta option to prevent accidental usage.");
|
||||
|
||||
betaOption.indexList[0].negate = false;
|
||||
|
||||
TEST_RESULT_VOID(parseOptionBeta(cfgOptRepoPath, 0, true, &betaOption), "beta option set");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("dependent option missing");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user