1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-06-20 01:17:49 +02:00

Use roles to simplify option command lists.

In many cases the valid commands are based on the commands valid for roles. In these cases derive the commands from a role list rather than an explicit command list.

Not only is this notation more compact but it helps prevent new commands from being missed.

This exposed a few issues:

1) The cmd option should only be valid when a command supports the local role since it is used to execute the local process. A number of commands were included before that did not have the local role.

2) cmd-ssh should be valid for any command that allows remotes. The annotate command was missing from this list.

3) compress-level-network should be valid for any command that allows remotes. The repo-rm command was missing from this list.
This commit is contained in:
David Steele
2025-11-11 09:44:41 +02:00
parent 411c1d0a87
commit 9653d444f2
4 changed files with 99 additions and 132 deletions
+10 -59
View File
@@ -709,22 +709,8 @@ option:
default-type: dynamic
default: bin
command:
archive-get: {}
archive-push: {}
backup: {}
check: {}
expire: {}
info: {}
manifest: {}
repo-get: {}
repo-ls: {}
repo-put: {}
repo-rm: {}
restore: {}
stanza-create: {}
stanza-delete: {}
stanza-upgrade: {}
verify: {}
+role: async
+role: local
command-role:
async: {}
main: {}
@@ -735,6 +721,8 @@ option:
required: true
default-type: quote
default: ssh
command:
+role: remote
# Option is deprecated and should not be referenced outside of cfgLoadUpdateOption()
compress:
@@ -780,22 +768,7 @@ option:
default: 1
allow-range: [-5, 12]
command:
annotate: {}
archive-get: {}
archive-push: {}
backup: {}
check: {}
expire: {}
info: {}
manifest: {}
repo-get: {}
repo-ls: {}
repo-put: {}
restore: {}
stanza-create: {}
stanza-delete: {}
stanza-upgrade: {}
verify: {}
+role: remote
command-role:
async: {}
main: {}
@@ -877,11 +850,7 @@ option:
default: 15s
allow-range: [0s, 15m]
command:
archive-get: {}
archive-push: {}
backup: {}
restore: {}
verify: {}
+role: local
lock-path:
section: global
@@ -930,11 +899,7 @@ option:
default: 1
allow-range: [1, 999]
command:
archive-get: {}
archive-push: {}
backup: {}
restore: {}
verify: {}
+role: local
command-role:
async: {}
main: {}
@@ -1104,23 +1069,9 @@ option:
type: boolean
default: false
command:
annotate: {}
archive-get: {}
archive-push: {}
backup: {}
check: {}
expire: {}
info: {}
manifest: {}
repo-get: {}
repo-ls: {}
repo-put: {}
repo-rm: {}
restore: {}
stanza-create: {}
stanza-delete: {}
stanza-upgrade: {}
verify: {}
+role: async
+role: local
+role: remote
log-timestamp:
section: global
+84 -62
View File
@@ -856,7 +856,7 @@ bldCfgParseOptionDeprecateReconcile(const List *const optDeprecateRawList)
// Helper to parse the option command list
static const List *
bldCfgParseOptionCommandList(Yaml *const yaml, const List *const optList)
bldCfgParseOptionCommandList(Yaml *const yaml, const List *const cmdList, const List *const optList)
{
const List *result = NULL;
@@ -876,78 +876,100 @@ bldCfgParseOptionCommandList(Yaml *const yaml, const List *const optList)
yamlEventCheck(optCmd, yamlEventTypeScalar);
BldCfgOptionCommandRaw optCmdRaw = {.name = optCmd.value};
yamlEventNextCheck(yaml, yamlEventTypeMapBegin);
YamlEvent optCmdDef = yamlEventNext(yaml);
if (optCmdDef.type == yamlEventTypeScalar)
if (strEqZ(optCmd.value, "+role"))
{
do
YamlEvent cmdRoleVal = yamlEventNext(yaml);
yamlEventCheck(cmdRoleVal, yamlEventTypeScalar);
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(cmdList); cmdIdx++)
{
yamlEventCheck(optCmdDef, yamlEventTypeScalar);
const BldCfgCommand *const cmd = lstGet(cmdList, cmdIdx);
if (strEqZ(optCmdDef.value, "allow-list"))
for (unsigned int cmdRoleIdx = 0; cmdRoleIdx < strLstSize(cmd->roleList); cmdRoleIdx++)
{
optCmdRaw.allowList = bldCfgParseAllowList(yaml, NULL);
}
else if (strEqZ(optCmdDef.value, "command-role"))
{
optCmdRaw.roleList = bldCfgParseCommandRole(yaml);
}
else if (strEqZ(optCmdDef.value, "depend"))
{
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
optCmdRaw.depend = bldCfgParseDepend(yaml, optList);
}
MEM_CONTEXT_END();
}
else if (strEqZ(optCmdDef.value, "default"))
{
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
optCmdRaw.defaultValue = bldCfgParseDefault(yaml);
}
MEM_CONTEXT_END();
}
else
{
YamlEvent optCmdDefVal = yamlEventNextCheck(yaml, yamlEventTypeScalar);
const String *role = strLstGet(cmd->roleList, cmdRoleIdx);
BldCfgOptionCommandRaw optCmdRaw = {.name = cmd->name};
if (strEqZ(optCmdDef.value, "internal"))
{
optCmdRaw.internal = varNewBool(yamlBoolParse(optCmdDefVal));
}
else if (strEqZ(optCmdDef.value, "required"))
{
optCmdRaw.required = varNewBool(yamlBoolParse(optCmdDefVal));
}
else
THROW_FMT(FormatError, "unknown option command definition '%s'", strZ(optCmdDef.value));
if (strEq(role, cmdRoleVal.value) && !lstFind(optCmdRawList, &optCmdRaw))
lstAdd(optCmdRawList, &optCmdRaw);
}
optCmdDef = yamlEventNext(yaml);
}
while (optCmdDef.type != yamlEventTypeMapEnd);
}
else
yamlEventCheck(optCmdDef, yamlEventTypeMapEnd);
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
const BldCfgOptionCommandRaw bldCfgOptionCommandRaw =
{
.name = strDup(optCmdRaw.name),
.internal = varDup(optCmdRaw.internal),
.required = varDup(optCmdRaw.required),
.defaultValue = optCmdRaw.defaultValue,
.depend = optCmdRaw.depend,
.allowList = bldCfgParseAllowListDup(optCmdRaw.allowList),
.roleList = strLstDup(optCmdRaw.roleList),
};
yamlEventNextCheck(yaml, yamlEventTypeMapBegin);
YamlEvent optCmdDef = yamlEventNext(yaml);
lstAdd(optCmdRawList, &bldCfgOptionCommandRaw);
if (optCmdDef.type == yamlEventTypeScalar)
{
do
{
yamlEventCheck(optCmdDef, yamlEventTypeScalar);
if (strEqZ(optCmdDef.value, "allow-list"))
{
optCmdRaw.allowList = bldCfgParseAllowList(yaml, NULL);
}
else if (strEqZ(optCmdDef.value, "command-role"))
{
optCmdRaw.roleList = bldCfgParseCommandRole(yaml);
}
else if (strEqZ(optCmdDef.value, "depend"))
{
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
optCmdRaw.depend = bldCfgParseDepend(yaml, optList);
}
MEM_CONTEXT_END();
}
else if (strEqZ(optCmdDef.value, "default"))
{
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
optCmdRaw.defaultValue = bldCfgParseDefault(yaml);
}
MEM_CONTEXT_END();
}
else
{
YamlEvent optCmdDefVal = yamlEventNextCheck(yaml, yamlEventTypeScalar);
if (strEqZ(optCmdDef.value, "internal"))
{
optCmdRaw.internal = varNewBool(yamlBoolParse(optCmdDefVal));
}
else if (strEqZ(optCmdDef.value, "required"))
{
optCmdRaw.required = varNewBool(yamlBoolParse(optCmdDefVal));
}
else
THROW_FMT(FormatError, "unknown option command definition '%s'", strZ(optCmdDef.value));
}
optCmdDef = yamlEventNext(yaml);
}
while (optCmdDef.type != yamlEventTypeMapEnd);
}
else
yamlEventCheck(optCmdDef, yamlEventTypeMapEnd);
MEM_CONTEXT_BEGIN(lstMemContext(optCmdRawList))
{
const BldCfgOptionCommandRaw bldCfgOptionCommandRaw =
{
.name = strDup(optCmdRaw.name),
.internal = varDup(optCmdRaw.internal),
.required = varDup(optCmdRaw.required),
.defaultValue = optCmdRaw.defaultValue,
.depend = optCmdRaw.depend,
.allowList = bldCfgParseAllowListDup(optCmdRaw.allowList),
.roleList = strLstDup(optCmdRaw.roleList),
};
lstAdd(optCmdRawList, &bldCfgOptionCommandRaw);
}
MEM_CONTEXT_END();
}
MEM_CONTEXT_END();
optCmd = yamlEventNext(yaml);
}
@@ -1014,7 +1036,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
}
else if (strEqZ(optDef.value, "command"))
{
optRaw.cmdList = bldCfgParseOptionCommandList(yaml, optListRaw);
optRaw.cmdList = bldCfgParseOptionCommandList(yaml, cmdList, optListRaw);
}
else if (strEqZ(optDef.value, "command-role"))
{
+2 -11
View File
@@ -1721,18 +1721,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/cmd
PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Backup) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Check) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Expire) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Info) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Manifest) // opt/cmd
PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/cmd
PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/cmd
PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/cmd
PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Restore) // opt/cmd
PARSE_RULE_OPTION_COMMAND(StanzaCreate) // opt/cmd
PARSE_RULE_OPTION_COMMAND(StanzaDelete) // opt/cmd
PARSE_RULE_OPTION_COMMAND(StanzaUpgrade) // opt/cmd
PARSE_RULE_OPTION_COMMAND(Verify) // opt/cmd
), // opt/cmd
// opt/cmd
@@ -1773,6 +1762,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
// opt/cmd-ssh
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/cmd-ssh
( // opt/cmd-ssh
PARSE_RULE_OPTION_COMMAND(Annotate) // opt/cmd-ssh
PARSE_RULE_OPTION_COMMAND(ArchiveGet) // opt/cmd-ssh
PARSE_RULE_OPTION_COMMAND(ArchivePush) // opt/cmd-ssh
PARSE_RULE_OPTION_COMMAND(Backup) // opt/cmd-ssh
@@ -1936,6 +1926,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
PARSE_RULE_OPTION_COMMAND(RepoGet) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(RepoLs) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(RepoPut) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(RepoRm) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(Restore) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(StanzaCreate) // opt/compress-level-network
PARSE_RULE_OPTION_COMMAND(StanzaDelete) // opt/compress-level-network
+3
View File
@@ -378,6 +378,9 @@ testRun(void)
" type: path\n"
" default-type: quote\n"
" default: /include\n"
" command:\n"
" +role: local\n"
" +role: remote\n"
" command-role:\n"
" main: {}\n"
"\n"