Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
609b3dcf9d |
3 changed files with 37 additions and 21 deletions
|
|
@ -1,12 +1,16 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, override
|
from argparse import ArgumentParser, ArgumentTypeError, Namespace
|
||||||
|
from typing import override
|
||||||
|
|
||||||
from .Cmd import Cmd, Parent
|
from .Cmd import Cmd, Parent
|
||||||
from .lib.base import Attrs
|
from .lib.base import Attrs
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
def octal_mode(s: str) -> int:
|
||||||
from argparse import ArgumentParser, Namespace
|
try:
|
||||||
|
return int(s, 8)
|
||||||
|
except ValueError:
|
||||||
|
raise ArgumentTypeError(f'Not an octal mode: {s}')
|
||||||
|
|
||||||
class CmdCompileTemplates(Cmd): # export
|
class CmdCompileTemplates(Cmd): # export
|
||||||
|
|
||||||
|
|
@ -30,5 +34,9 @@ class CmdCompileTemplates(Cmd): # export
|
||||||
'--group', '-g', default = None, help = 'Default output file group'
|
'--group', '-g', default = None, help = 'Default output file group'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--mode', '-m', default = None, help = 'Default output file mode'
|
'--mode',
|
||||||
|
'-m',
|
||||||
|
default = None,
|
||||||
|
type = octal_mode,
|
||||||
|
help = 'Default output file mode (octal)',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,10 @@ class FilesContext:
|
||||||
elif key == 'group':
|
elif key == 'group':
|
||||||
ret.group = val or None
|
ret.group = val or None
|
||||||
elif key == 'mode':
|
elif key == 'mode':
|
||||||
|
# -- octal, with or without a leading zero
|
||||||
if val:
|
if val:
|
||||||
try:
|
try:
|
||||||
if re.fullmatch(r'0[0-7]+', val):
|
ret.mode = int(val, 8)
|
||||||
ret.mode = int(val, 8)
|
|
||||||
else:
|
|
||||||
ret.mode = int(val, 0)
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
ret.mode = None
|
ret.mode = None
|
||||||
|
|
||||||
|
|
@ -126,6 +124,11 @@ class FilesContext:
|
||||||
attrs = self._parse_attributes(new_content)
|
attrs = self._parse_attributes(new_content)
|
||||||
if attrs is None:
|
if attrs is None:
|
||||||
attrs = default_attrs
|
attrs = default_attrs
|
||||||
|
else:
|
||||||
|
# -- A conf line is the attribute spec of the output
|
||||||
|
# file, not part of its content
|
||||||
|
lines = new_content.splitlines(keepends = True)
|
||||||
|
new_content = ''.join(lines[1:])
|
||||||
|
|
||||||
if attrs is not None:
|
if attrs is not None:
|
||||||
if attrs.owner is not None:
|
if attrs.owner is not None:
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ def main() -> None:
|
||||||
os.makedirs(target + '/etc')
|
os.makedirs(target + '/etc')
|
||||||
with open(target + '/etc/foo.conf.jw-tmpl', 'w') as f:
|
with open(target + '/etc/foo.conf.jw-tmpl', 'w') as f:
|
||||||
f.write(
|
f.write(
|
||||||
f'# conf: owner={USER}, group={GROUP}, mode=0640\n'
|
f'# conf: owner={USER}, group={GROUP}, mode=640\n'
|
||||||
'host=HOSTNAME\n'
|
'host=HOSTNAME\n'
|
||||||
'token=TOKEN\n'
|
'token=TOKEN\n'
|
||||||
)
|
)
|
||||||
|
|
@ -297,8 +297,15 @@ def main() -> None:
|
||||||
assert not os.path.exists(baz_secret)
|
assert not os.path.exists(baz_secret)
|
||||||
assert stat.S_IMODE(os.stat(foo_secret).st_mode) == 0o400
|
assert stat.S_IMODE(os.stat(foo_secret).st_mode) == 0o400
|
||||||
|
|
||||||
# -- compile-templates: substitute the keys, apply the metadata
|
# -- The file attributes can also be specified atop the
|
||||||
|
# .jw-secret; bar takes its mode from there
|
||||||
|
os.unlink(bar_secret)
|
||||||
|
with open(bar_secret, 'w') as f:
|
||||||
|
f.write(f'# conf: owner={USER}, group={GROUP}, mode=640\n')
|
||||||
|
f.write('PLAIN=plain-value\n')
|
||||||
|
|
||||||
|
# -- compile-templates: substitute the keys, apply the
|
||||||
|
# metadata; the conf lines win over the --mode default
|
||||||
out, _ = run_cli(
|
out, _ = run_cli(
|
||||||
distro, [
|
distro, [
|
||||||
'secrets',
|
'secrets',
|
||||||
|
|
@ -307,6 +314,8 @@ def main() -> None:
|
||||||
USER,
|
USER,
|
||||||
'--group',
|
'--group',
|
||||||
GROUP,
|
GROUP,
|
||||||
|
'--mode',
|
||||||
|
'400',
|
||||||
PKG,
|
PKG,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -314,13 +323,10 @@ def main() -> None:
|
||||||
st = os.stat(foo_out)
|
st = os.stat(foo_out)
|
||||||
assert (st.st_uid, stat.S_IMODE(st.st_mode)) == (os.getuid(), 0o640), st
|
assert (st.st_uid, stat.S_IMODE(st.st_mode)) == (os.getuid(), 0o640), st
|
||||||
with open(foo_out) as f:
|
with open(foo_out) as f:
|
||||||
assert f.read() == (
|
assert f.read() == ('host=jannet\n'
|
||||||
f'# conf: owner={USER}, group={GROUP}, mode=0640\n'
|
'token=host-token\n')
|
||||||
'host=jannet\n'
|
|
||||||
'token=host-token\n'
|
|
||||||
)
|
|
||||||
st = os.stat(bar_out)
|
st = os.stat(bar_out)
|
||||||
assert (st.st_uid, stat.S_IMODE(st.st_mode)) == (os.getuid(), 0o400), st
|
assert (st.st_uid, stat.S_IMODE(st.st_mode)) == (os.getuid(), 0o640), st
|
||||||
with open(bar_out) as f:
|
with open(bar_out) as f:
|
||||||
assert f.read() == 'plain=plain-value\n'
|
assert f.read() == 'plain=plain-value\n'
|
||||||
|
|
||||||
|
|
@ -334,16 +340,15 @@ def main() -> None:
|
||||||
USER,
|
USER,
|
||||||
'--group',
|
'--group',
|
||||||
GROUP,
|
GROUP,
|
||||||
|
'--mode',
|
||||||
|
'400',
|
||||||
PKG,
|
PKG,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert out == [], out
|
assert out == [], out
|
||||||
with open(foo_out) as f:
|
with open(foo_out) as f:
|
||||||
assert f.read() == (
|
assert f.read() == ('host=jannet\n'
|
||||||
f'# conf: owner={USER}, group={GROUP}, mode=0640\n'
|
'token=host-token\n')
|
||||||
'host=jannet\n'
|
|
||||||
'token=host-token\n'
|
|
||||||
)
|
|
||||||
with open(bar_out) as f:
|
with open(bar_out) as f:
|
||||||
assert f.read() == 'plain=plain-value\n'
|
assert f.read() == 'plain=plain-value\n'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue