diff --git a/doc/lib/pgBackRestDoc/Common/DocConfig.pm b/doc/lib/pgBackRestDoc/Common/DocConfig.pm index fbf7d9ff1..a0a087086 100644 --- a/doc/lib/pgBackRestDoc/Common/DocConfig.pm +++ b/doc/lib/pgBackRestDoc/Common/DocConfig.pm @@ -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}"; diff --git a/doc/lib/pgBackRestDoc/Custom/DocConfigData.pm b/doc/lib/pgBackRestDoc/Custom/DocConfigData.pm index e2bb49d94..64aff74b4 100644 --- a/doc/lib/pgBackRestDoc/Custom/DocConfigData.pm +++ b/doc/lib/pgBackRestDoc/Custom/DocConfigData.pm @@ -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'; diff --git a/src/build/config/config.yaml b/src/build/config/config.yaml index c225e853f..cbafc33c4 100644 --- a/src/build/config/config.yaml +++ b/src/build/config/config.yaml @@ -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 diff --git a/src/build/config/parse.c b/src/build/config/parse.c index e0f9eb0ef..d7d918e4d 100644 --- a/src/build/config/parse.c +++ b/src/build/config/parse.c @@ -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, diff --git a/src/build/config/parse.h b/src/build/config/parse.h index fc1ababaf..392ac0344 100644 --- a/src/build/config/parse.h +++ b/src/build/config/parse.h @@ -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? diff --git a/src/build/config/render.c b/src/build/config/render.c index 466a84d0d..c3f1ac704 100644 --- a/src/build/config/render.c +++ b/src/build/config/render.c @@ -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"); diff --git a/src/build/help/help.xml b/src/build/help/help.xml index f10a1d92c..d4019acb2 100644 --- a/src/build/help/help.xml +++ b/src/build/help/help.xml @@ -151,6 +151,16 @@ + + Allow beta features. + + +

Allow beta features to be enabled but do not actually enable them. Beta features need to be separately enabled using related options.

+
+ + 2MiB +
+ Buffer size for I/O operations. diff --git a/src/command/help/help.c b/src/command/help/help.c index c5986b94a..4107365d6 100644 --- a/src/command/help/help.c +++ b/src/command/help/help.c @@ -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); diff --git a/src/config/config.auto.h b/src/config/config.auto.h index b2c57679a..5c2da9fd1 100644 --- a/src/config/config.auto.h +++ b/src/config/config.auto.h @@ -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, diff --git a/src/config/parse.auto.c.inc b/src/config/parse.auto.c.inc index d971a03af..af06bfa0e 100644 --- a/src/config/parse.auto.c.inc +++ b/src/config/parse.auto.c.inc @@ -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 diff --git a/src/config/parse.c b/src/config/parse.c index 51b9a3de6..31564ac4a 100644 --- a/src/config/parse.c +++ b/src/config/parse.c @@ -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; diff --git a/src/config/parse.h b/src/config/parse.h index 946cc3e7c..c97041c04 100644 --- a/src/config/parse.h +++ b/src/config/parse.h @@ -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, ...) \ diff --git a/test/lib/pgBackRestTest/Env/Host/HostBackupTest.pm b/test/lib/pgBackRestTest/Env/Host/HostBackupTest.pm index 17eda5a0a..968efcffb 100644 --- a/test/lib/pgBackRestTest/Env/Host/HostBackupTest.pm +++ b/test/lib/pgBackRestTest/Env/Host/HostBackupTest.pm @@ -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); diff --git a/test/src/build/config/config.yaml b/test/src/build/config/config.yaml index afdc03025..a12fc79cd 100644 --- a/test/src/build/config/config.yaml +++ b/test/src/build/config/config.yaml @@ -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} diff --git a/test/src/build/help/help.xml b/test/src/build/help/help.xml index 37371f660..190179895 100644 --- a/test/src/build/help/help.xml +++ b/test/src/build/help/help.xml @@ -6,6 +6,7 @@ + diff --git a/test/src/common/harnessConfig.c b/test/src/common/harnessConfig.c index 9f60e1838..2898ecd72 100644 --- a/test/src/common/harnessConfig.c +++ b/test/src/common/harnessConfig.c @@ -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)); diff --git a/test/src/module/build/configTest.c b/test/src/module/build/configTest.c index 2ad189179..83454c7c8 100644 --- a/test/src/module/build/configTest.c +++ b/test/src/module/build/configTest.c @@ -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" diff --git a/test/src/module/command/helpTest.c b/test/src/module/command/helpTest.c index 47908f6ff..3c6f120a7 100644 --- a/test/src/module/command/helpTest.c +++ b/test/src/module/command/helpTest.c @@ -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" diff --git a/test/src/module/command/restoreTest.c b/test/src/module/command/restoreTest.c index 2a358d721..0648d3024 100644 --- a/test/src/module/command/restoreTest.c +++ b/test/src/module/command/restoreTest.c @@ -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"); // ------------------------------------------------------------------------------------------------------------------------- diff --git a/test/src/module/config/parseTest.c b/test/src/module/config/parseTest.c index af1284ade..224a26b07 100644 --- a/test/src/module/config/parseTest.c +++ b/test/src/module/config/parseTest.c @@ -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");