fix: an upgrade keeps the configuration and asks nothing.
Release / release (push) Successful in 18s
Release / release (push) Successful in 18s
This commit is contained in:
1 parent
08693f7e68
commit
cda33fc4c7
4 files changed
+81
-37
No files matched your search
@@ -6,18 +6,15 @@ Called from postinst, which hands the result to ucf(1). ucf compares it with
|
||||
this script never writes to /etc and never has to worry about destroying an
|
||||
administrator's edits.
|
||||
|
||||
The candidate is built in three layers, each overriding the one before:
|
||||
|
||||
1. the shipped defaults
|
||||
2. the current contents of /etc/vpn-router/vpn-router.json, if it exists
|
||||
3. any debconf answer that was actually given
|
||||
|
||||
Layer 2 is what makes reconfiguration safe: a setting the administrator edited
|
||||
by hand, and that debconf has nothing to say about, is carried through
|
||||
unchanged, so ucf only sees a diff where something really changed. An empty
|
||||
answer means "leave alone", which is why clearing the pre-shared key from the
|
||||
debconf database after installation does not blank it on the next
|
||||
dpkg-reconfigure.
|
||||
The candidate is the current /etc/vpn-router/vpn-router.json as it is, or the
|
||||
shipped defaults when there is none, with every debconf answer that was given
|
||||
written over it. Without an answer that changes it, the candidate is the current
|
||||
file byte for byte, so
|
||||
an upgrade or a reconfiguration that changes nothing gives ucf nothing to ask;
|
||||
defaults the file leaves out are applied by vpnrouter.load_config() when the
|
||||
file is read. An empty answer means "leave alone", which is why clearing the
|
||||
pre-shared key from the debconf database after installation does not blank it
|
||||
on the next dpkg-reconfigure.
|
||||
|
||||
Answers arrive as VPN_ROUTER_* environment variables and are converted to the
|
||||
type of the setting they fill: comma-separated lists become arrays, "true" and
|
||||
@@ -62,8 +59,21 @@ def main():
|
||||
return 2
|
||||
output = sys.argv[1]
|
||||
|
||||
cfg = vpnrouter.load_config()
|
||||
try:
|
||||
current = vpnrouter.CONFIG_FILE.read_text(encoding='utf-8')
|
||||
except FileNotFoundError:
|
||||
current = None
|
||||
if current is None:
|
||||
cfg = vpnrouter.load_config()
|
||||
else:
|
||||
try:
|
||||
cfg = json.loads(current)
|
||||
except ValueError as exc:
|
||||
vpnrouter.fail(f'cannot parse {vpnrouter.CONFIG_FILE}: {exc}')
|
||||
if not isinstance(cfg, dict):
|
||||
vpnrouter.fail(f'cannot parse {vpnrouter.CONFIG_FILE}: the top level is not an object')
|
||||
|
||||
answered = False
|
||||
for variable, (section, option) in ANSWERS.items():
|
||||
value = os.environ.get(variable)
|
||||
if not value:
|
||||
@@ -76,11 +86,19 @@ def main():
|
||||
value = int(value) if value.isdigit() else value
|
||||
elif isinstance(default, list):
|
||||
value = [item.strip() for item in value.split(',') if item.strip()]
|
||||
cfg[section][option] = value
|
||||
if not isinstance(cfg.get(section, {}), dict):
|
||||
vpnrouter.fail(f'cannot parse {vpnrouter.CONFIG_FILE}: {section} is not an object')
|
||||
if cfg.setdefault(section, {}).get(option) != value:
|
||||
cfg[section][option] = value
|
||||
answered = True
|
||||
|
||||
# Without a change the candidate is the current file byte for byte, as ucf compares checksums.
|
||||
with open(output, 'w', encoding='utf-8') as handle:
|
||||
json.dump(cfg, handle, indent=2)
|
||||
handle.write('\n')
|
||||
if current is not None and not answered:
|
||||
handle.write(current)
|
||||
else:
|
||||
json.dump(cfg, handle, indent=2)
|
||||
handle.write('\n')
|
||||
os.chmod(output, 0o600)
|
||||
return 0
|
||||
|
||||
|
||||
Reference in new issue
Block a user