Fix environment variable interpolation within configured repository paths (#782).
This commit is contained in:
parent
2da43239f6
commit
6cc93c4eb9
4 changed files with 29 additions and 18 deletions
1
NEWS
1
NEWS
|
@ -8,6 +8,7 @@
|
||||||
overriding the existing "archive_name_format" and "match_archives" options in configuration.
|
overriding the existing "archive_name_format" and "match_archives" options in configuration.
|
||||||
* #779: Only parse "--override" values as complex data types when they're for options of those
|
* #779: Only parse "--override" values as complex data types when they're for options of those
|
||||||
types.
|
types.
|
||||||
|
* #782: Fix environment variable interpolation within configured repository paths.
|
||||||
|
|
||||||
1.8.4
|
1.8.4
|
||||||
* #715: Add a monitoring hook for sending backup status to a variety of monitoring services via the
|
* #715: Add a monitoring hook for sending backup status to a variety of monitoring services via the
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
_VARIABLE_PATTERN = re.compile(
|
VARIABLE_PATTERN = re.compile(
|
||||||
r'(?P<escape>\\)?(?P<variable>\$\{(?P<name>[A-Za-z0-9_]+)((:?-)(?P<default>[^}]+))?\})'
|
r'(?P<escape>\\)?(?P<variable>\$\{(?P<name>[A-Za-z0-9_]+)((:?-)(?P<default>[^}]+))?\})'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_string(matcher):
|
def resolve_string(matcher):
|
||||||
'''
|
'''
|
||||||
Get the value from environment given a matcher containing a name and an optional default value.
|
Given a matcher containing a name and an optional default value, get the value from environment.
|
||||||
If the variable is not defined in environment and no default value is provided, an Error is raised.
|
|
||||||
|
Raise ValueError if the variable is not defined in environment and no default value is provided.
|
||||||
'''
|
'''
|
||||||
if matcher.group('escape') is not None:
|
if matcher.group('escape') is not None:
|
||||||
# in case of escaped envvar, unescape it
|
# In the case of an escaped environment variable, unescape it.
|
||||||
return matcher.group('variable')
|
return matcher.group('variable')
|
||||||
|
|
||||||
# resolve the env var
|
# Resolve the environment variable.
|
||||||
name, default = matcher.group('name'), matcher.group('default')
|
name, default = matcher.group('name'), matcher.group('default')
|
||||||
out = os.getenv(name, default=default)
|
out = os.getenv(name, default=default)
|
||||||
|
|
||||||
|
@ -27,19 +28,24 @@ def _resolve_string(matcher):
|
||||||
|
|
||||||
def resolve_env_variables(item):
|
def resolve_env_variables(item):
|
||||||
'''
|
'''
|
||||||
Resolves variables like or ${FOO} from given configuration with values from process environment
|
Resolves variables like or ${FOO} from given configuration with values from process environment.
|
||||||
Supported formats:
|
|
||||||
- ${FOO} will return FOO env variable
|
|
||||||
- ${FOO-bar} or ${FOO:-bar} will return FOO env variable if it exists, else "bar"
|
|
||||||
|
|
||||||
If any variable is missing in environment and no default value is provided, an Error is raised.
|
Supported formats:
|
||||||
|
|
||||||
|
* ${FOO} will return FOO env variable
|
||||||
|
* ${FOO-bar} or ${FOO:-bar} will return FOO env variable if it exists, else "bar"
|
||||||
|
|
||||||
|
Raise if any variable is missing in environment and no default value is provided.
|
||||||
'''
|
'''
|
||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
return _VARIABLE_PATTERN.sub(_resolve_string, item)
|
return VARIABLE_PATTERN.sub(resolve_string, item)
|
||||||
|
|
||||||
if isinstance(item, list):
|
if isinstance(item, list):
|
||||||
for i, subitem in enumerate(item):
|
for index, subitem in enumerate(item):
|
||||||
item[i] = resolve_env_variables(subitem)
|
item[index] = resolve_env_variables(subitem)
|
||||||
|
|
||||||
if isinstance(item, dict):
|
if isinstance(item, dict):
|
||||||
for key, value in item.items():
|
for key, value in item.items():
|
||||||
item[key] = resolve_env_variables(value)
|
item[key] = resolve_env_variables(value)
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
|
@ -110,10 +110,12 @@ def parse_configuration(config_filename, schema_filename, overrides=None, resolv
|
||||||
raise Validation_error(config_filename, (str(error),))
|
raise Validation_error(config_filename, (str(error),))
|
||||||
|
|
||||||
override.apply_overrides(config, schema, overrides)
|
override.apply_overrides(config, schema, overrides)
|
||||||
logs = normalize.normalize(config_filename, config)
|
|
||||||
if resolve_env:
|
if resolve_env:
|
||||||
environment.resolve_env_variables(config)
|
environment.resolve_env_variables(config)
|
||||||
|
|
||||||
|
logs = normalize.normalize(config_filename, config)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
validator = jsonschema.Draft7Validator(schema)
|
validator = jsonschema.Draft7Validator(schema)
|
||||||
except AttributeError: # pragma: no cover
|
except AttributeError: # pragma: no cover
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -244,7 +245,7 @@ def test_parse_configuration_applies_overrides():
|
||||||
assert logs == []
|
assert logs == []
|
||||||
|
|
||||||
|
|
||||||
def test_parse_configuration_applies_normalization():
|
def test_parse_configuration_applies_normalization_after_environment_variable_interpolation():
|
||||||
mock_config_and_schema(
|
mock_config_and_schema(
|
||||||
'''
|
'''
|
||||||
location:
|
location:
|
||||||
|
@ -252,17 +253,18 @@ def test_parse_configuration_applies_normalization():
|
||||||
- /home
|
- /home
|
||||||
|
|
||||||
repositories:
|
repositories:
|
||||||
- path: hostname.borg
|
- ${NO_EXIST:-user@hostname:repo}
|
||||||
|
|
||||||
exclude_if_present: .nobackup
|
exclude_if_present: .nobackup
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
flexmock(os).should_receive('getenv').replace_with(lambda variable_name, default: default)
|
||||||
|
|
||||||
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
|
config, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
|
||||||
|
|
||||||
assert config == {
|
assert config == {
|
||||||
'source_directories': ['/home'],
|
'source_directories': ['/home'],
|
||||||
'repositories': [{'path': 'hostname.borg'}],
|
'repositories': [{'path': 'ssh://user@hostname/./repo'}],
|
||||||
'exclude_if_present': ['.nobackup'],
|
'exclude_if_present': ['.nobackup'],
|
||||||
}
|
}
|
||||||
assert logs
|
assert logs
|
||||||
|
|
Loading…
Reference in a new issue