Parser: fix template inheritance
This commit is contained in:
parent
fab1f94faa
commit
bd65674875
2 changed files with 60 additions and 27 deletions
|
@ -169,10 +169,12 @@ orca:
|
||||||
range:
|
range:
|
||||||
from: 9026
|
from: 9026
|
||||||
to: 9042
|
to: 9042
|
||||||
template: LocalForward ${port} localhost:${port}
|
property: LocalForward
|
||||||
|
template: ${port} localhost:${port}
|
||||||
- variable: env
|
- variable: env
|
||||||
range: ["FOO=bar", "BAR=foo"]
|
range: ["FOO=bar", "BAR=foo"]
|
||||||
template: SendEnv ${env}
|
property: SendEnv
|
||||||
|
template: ${env}
|
||||||
```
|
```
|
||||||
|
|
||||||
Expands to:
|
Expands to:
|
||||||
|
|
81
src/main.py
81
src/main.py
|
@ -6,7 +6,7 @@ from pathlib import Path
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Process some integers.")
|
parser = argparse.ArgumentParser(description="Process some integers.")
|
||||||
parser.add_argument("file", type=Path, help="yaml config")
|
parser.add_argument("file", type=Path, help="yaml config")
|
||||||
parser.add_argument("--json", action="store_true", help="output as json")
|
parser.add_argument("--json", action="store_true", help="output parse as json")
|
||||||
parser.add_argument("--out", type=Path, help="write into file")
|
parser.add_argument("--out", type=Path, help="write into file")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -66,23 +66,57 @@ def expand_for_loop(props):
|
||||||
return expand
|
return expand
|
||||||
|
|
||||||
|
|
||||||
def get_ssh_props(data, hosts):
|
def get_ssh_props(data):
|
||||||
props = list()
|
props = dict()
|
||||||
|
|
||||||
if data.get('template'):
|
|
||||||
props = list(hosts[data.get('template')])
|
|
||||||
|
|
||||||
for k, v in data["ssh_props"].items():
|
for k, v in data["ssh_props"].items():
|
||||||
if isinstance(v, list):
|
props[k] = v if isinstance(v, list) else [v]
|
||||||
for x in v:
|
|
||||||
props.append(f"{k} {x}")
|
|
||||||
else:
|
|
||||||
props.append(f"{k} {v}")
|
|
||||||
|
|
||||||
return props
|
return props
|
||||||
|
|
||||||
|
|
||||||
def remove_templates(expanded, data):
|
def parse_props(host, props, hosts):
|
||||||
|
parsed = dict()
|
||||||
|
|
||||||
|
# Standard ssh properties
|
||||||
|
for k, v in props.get("ssh_props", {}).items():
|
||||||
|
parsed[k] = v if isinstance(v, list) else [v]
|
||||||
|
|
||||||
|
# For-loops in config
|
||||||
|
for loop in props.get("for", []):
|
||||||
|
prop = loop["property"]
|
||||||
|
r = loop["range"]
|
||||||
|
|
||||||
|
if isinstance(r, dict):
|
||||||
|
iterations = range(r["from"], r["to"])
|
||||||
|
elif isinstance(r, list):
|
||||||
|
iterations = r
|
||||||
|
else:
|
||||||
|
raise TypeError("Expected list or dict in `for` loop `range`")
|
||||||
|
|
||||||
|
if not parsed.get(prop):
|
||||||
|
parsed[prop] = list()
|
||||||
|
|
||||||
|
pattern = re.compile(rf'\$\{{{loop["variable"]}\}}')
|
||||||
|
|
||||||
|
for i in iterations:
|
||||||
|
sub = pattern.sub(str(i), loop["template"])
|
||||||
|
parsed[prop].append(sub)
|
||||||
|
|
||||||
|
# Inherit from template
|
||||||
|
template = props.get("template")
|
||||||
|
|
||||||
|
if template and template not in hosts:
|
||||||
|
raise SyntaxError(f"Template `{template}` required by `{host}` is not declared above `{host}` in the yaml")
|
||||||
|
elif template:
|
||||||
|
for k, v in hosts[props.get("template")].items():
|
||||||
|
if k not in parsed:
|
||||||
|
parsed[k] = v
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def remove_templates(expanded):
|
||||||
hosts = dict(expanded)
|
hosts = dict(expanded)
|
||||||
|
|
||||||
for host, props in data.items():
|
for host, props in data.items():
|
||||||
|
@ -109,21 +143,14 @@ def stringify_booleans(props):
|
||||||
|
|
||||||
|
|
||||||
def parse_yaml(data):
|
def parse_yaml(data):
|
||||||
expanded = dict()
|
hosts = dict()
|
||||||
check_duplicate_hosts(data)
|
check_duplicate_hosts(data)
|
||||||
|
|
||||||
for host, props in data.items():
|
for host, props in data.items():
|
||||||
parsed = list()
|
hosts[host] = parse_props(host, props, hosts)
|
||||||
parsed.extend(get_ssh_props(props, expanded))
|
|
||||||
parsed.extend(expand_for_loop(props))
|
|
||||||
|
|
||||||
parsed = stringify_booleans(parsed)
|
hosts = remove_templates(hosts)
|
||||||
run_assertions(host, parsed)
|
return hosts
|
||||||
|
|
||||||
expanded[host] = sorted(parsed)
|
|
||||||
|
|
||||||
expanded = remove_templates(expanded, data)
|
|
||||||
return expanded
|
|
||||||
|
|
||||||
|
|
||||||
# ╔───────────────────────────────────────────────────────────────────────────╗
|
# ╔───────────────────────────────────────────────────────────────────────────╗
|
||||||
|
@ -134,8 +161,12 @@ def to_ssh_config_string(parse):
|
||||||
|
|
||||||
for host, keys in parse.items():
|
for host, keys in parse.items():
|
||||||
s += f"Host {host}\n"
|
s += f"Host {host}\n"
|
||||||
for key in keys:
|
for key, values in keys.items():
|
||||||
s += f"\t{key}\n"
|
for value in values:
|
||||||
|
if isinstance(value, bool):
|
||||||
|
s += f"\t{key} {'true' if value else 'no'}\n"
|
||||||
|
else:
|
||||||
|
s += f"\t{key} {value}\n"
|
||||||
s += "\n"
|
s += "\n"
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
Loading…
Reference in a new issue