tft-cli 0.0.24__tar.gz → 0.0.25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tft_cli-0.0.24 → tft_cli-0.0.25}/PKG-INFO +1 -1
- {tft_cli-0.0.24 → tft_cli-0.0.25}/pyproject.toml +1 -1
- {tft_cli-0.0.24 → tft_cli-0.0.25}/src/tft/cli/commands.py +91 -18
- {tft_cli-0.0.24 → tft_cli-0.0.25}/src/tft/cli/utils.py +24 -0
- tft_cli-0.0.24/src/tft/cli/commands.py.backup +0 -2211
- {tft_cli-0.0.24 → tft_cli-0.0.25}/LICENSE +0 -0
- {tft_cli-0.0.24 → tft_cli-0.0.25}/LICENSE_SPDX +0 -0
- {tft_cli-0.0.24 → tft_cli-0.0.25}/src/tft/cli/__init__.py +0 -0
- {tft_cli-0.0.24 → tft_cli-0.0.25}/src/tft/cli/config.py +0 -0
- {tft_cli-0.0.24 → tft_cli-0.0.25}/src/tft/cli/tool.py +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import base64
|
|
5
|
+
import codecs
|
|
5
6
|
import ipaddress
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
@@ -27,6 +28,7 @@ from rich.table import Table
|
|
|
27
28
|
from tft.cli.config import settings
|
|
28
29
|
from tft.cli.utils import (
|
|
29
30
|
artifacts,
|
|
31
|
+
check_unexpected_arguments,
|
|
30
32
|
cmd_output_or_exit,
|
|
31
33
|
console,
|
|
32
34
|
console_stderr,
|
|
@@ -65,6 +67,11 @@ RESERVE_TMT_DISCOVER_EXTRA_ARGS = f"--insert --how fmf --url {RESERVE_URL} --ref
|
|
|
65
67
|
|
|
66
68
|
DEFAULT_PIPELINE_TIMEOUT = 60 * 12
|
|
67
69
|
|
|
70
|
+
# SSH command options for reservation connections
|
|
71
|
+
SSH_RESERVATION_OPTIONS = (
|
|
72
|
+
"ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oServerAliveInterval=60 -oServerAliveCountMax=3"
|
|
73
|
+
)
|
|
74
|
+
|
|
68
75
|
# Won't be validating CIDR and 65535 max port range with regex here, not worth it
|
|
69
76
|
SECURITY_GROUP_RULE_FORMAT = re.compile(r"(tcp|ip|icmp|udp|-1|[0-255]):(.*):(\d{1,5}-\d{1,5}|\d{1,5}|-1)")
|
|
70
77
|
|
|
@@ -82,6 +89,9 @@ class PipelineType(str, Enum):
|
|
|
82
89
|
ARGUMENT_API_URL: str = typer.Argument(
|
|
83
90
|
settings.API_URL, envvar="TESTING_FARM_API_URL", metavar='', rich_help_panel='Environment variables'
|
|
84
91
|
)
|
|
92
|
+
OPTION_API_URL: str = typer.Option(
|
|
93
|
+
settings.API_URL, envvar="TESTING_FARM_API_URL", metavar='', rich_help_panel='Environment variables'
|
|
94
|
+
)
|
|
85
95
|
ARGUMENT_API_TOKEN: str = typer.Argument(
|
|
86
96
|
settings.API_TOKEN,
|
|
87
97
|
envvar="TESTING_FARM_API_TOKEN",
|
|
@@ -89,6 +99,13 @@ ARGUMENT_API_TOKEN: str = typer.Argument(
|
|
|
89
99
|
metavar='',
|
|
90
100
|
rich_help_panel='Environment variables',
|
|
91
101
|
)
|
|
102
|
+
OPTION_API_TOKEN: str = typer.Option(
|
|
103
|
+
settings.API_TOKEN,
|
|
104
|
+
envvar="TESTING_FARM_API_TOKEN",
|
|
105
|
+
show_default=False,
|
|
106
|
+
metavar='',
|
|
107
|
+
rich_help_panel='Environment variables',
|
|
108
|
+
)
|
|
92
109
|
OPTION_TMT_PLAN_NAME: Optional[str] = typer.Option(
|
|
93
110
|
None,
|
|
94
111
|
"--plan",
|
|
@@ -326,12 +343,12 @@ def _sanity_reserve() -> None:
|
|
|
326
343
|
exit_error("No SSH identities found in the SSH agent. Please run `ssh-add`.")
|
|
327
344
|
|
|
328
345
|
|
|
329
|
-
def _handle_reservation(session, request_id: str, autoconnect: bool = False) -> None:
|
|
346
|
+
def _handle_reservation(session, api_url: str, request_id: str, autoconnect: bool = False) -> None:
|
|
330
347
|
"""
|
|
331
348
|
Handle the reservation for :py:func:``request`` and :py:func:``restart`` commands.
|
|
332
349
|
"""
|
|
333
350
|
# Get artifacts url
|
|
334
|
-
request_url = urllib.parse.urljoin(
|
|
351
|
+
request_url = urllib.parse.urljoin(api_url, f"/v0.1/requests/{request_id}")
|
|
335
352
|
response = session.get(request_url)
|
|
336
353
|
artifacts_url = response.json()['run']['artifacts']
|
|
337
354
|
|
|
@@ -386,7 +403,7 @@ def _handle_reservation(session, request_id: str, autoconnect: bool = False) ->
|
|
|
386
403
|
console.print(f"🌎 ssh root@{guests[0]}")
|
|
387
404
|
|
|
388
405
|
if autoconnect:
|
|
389
|
-
os.system(f"
|
|
406
|
+
os.system(f"{SSH_RESERVATION_OPTIONS} root@{guests[0]}") # noqa: E501
|
|
390
407
|
|
|
391
408
|
|
|
392
409
|
def _localhost_ingress_rule(session: requests.Session) -> str:
|
|
@@ -397,7 +414,7 @@ def _localhost_ingress_rule(session: requests.Session) -> str:
|
|
|
397
414
|
|
|
398
415
|
if get_ip.ok:
|
|
399
416
|
ip = get_ip.text.strip()
|
|
400
|
-
return f"-1:{ip}:-1"
|
|
417
|
+
return f"-1:{ip}:-1" # noqa: E231
|
|
401
418
|
|
|
402
419
|
else:
|
|
403
420
|
exit_error(f"Got {get_ip.status_code} while checking {settings.PUBLIC_IP_CHECKER_URL}")
|
|
@@ -666,21 +683,25 @@ def _print_summary_table(summary: dict, format: Optional[WatchFormat], show_deta
|
|
|
666
683
|
|
|
667
684
|
|
|
668
685
|
def watch(
|
|
669
|
-
|
|
686
|
+
context: typer.Context,
|
|
687
|
+
api_url: str = ARGUMENT_API_URL,
|
|
670
688
|
id: str = typer.Option(..., help="Request ID to watch"),
|
|
671
689
|
no_wait: bool = typer.Option(False, help="Skip waiting for request completion."),
|
|
672
690
|
format: Optional[WatchFormat] = typer.Option(WatchFormat.text, help="Output format"),
|
|
673
691
|
autoconnect: bool = typer.Option(True, hidden=True),
|
|
674
692
|
reserve: bool = typer.Option(False, hidden=True),
|
|
675
693
|
):
|
|
694
|
+
"""Watch request for completion."""
|
|
695
|
+
|
|
696
|
+
# Accept these arguments only via environment variables
|
|
697
|
+
check_unexpected_arguments(context, "api_url")
|
|
698
|
+
|
|
676
699
|
def _console_print(*args, **kwargs):
|
|
677
700
|
"""A helper function that will skip printing to console if output format is json"""
|
|
678
701
|
if format == WatchFormat.json:
|
|
679
702
|
return
|
|
680
703
|
console.print(*args, **kwargs)
|
|
681
704
|
|
|
682
|
-
"""Watch request for completion."""
|
|
683
|
-
|
|
684
705
|
if not uuid_valid(id):
|
|
685
706
|
exit_error("invalid request id")
|
|
686
707
|
|
|
@@ -737,7 +758,7 @@ def watch(
|
|
|
737
758
|
if state == current_state:
|
|
738
759
|
# check for reservation status and finish early if reserved
|
|
739
760
|
if reserve and _is_reserved(session, request):
|
|
740
|
-
_handle_reservation(session, request["id"], autoconnect)
|
|
761
|
+
_handle_reservation(session, api_url, request["id"], autoconnect)
|
|
741
762
|
return
|
|
742
763
|
|
|
743
764
|
time.sleep(1)
|
|
@@ -804,6 +825,7 @@ def version():
|
|
|
804
825
|
|
|
805
826
|
|
|
806
827
|
def request(
|
|
828
|
+
context: typer.Context,
|
|
807
829
|
api_url: str = ARGUMENT_API_URL,
|
|
808
830
|
api_token: str = ARGUMENT_API_TOKEN,
|
|
809
831
|
timeout: int = typer.Option(
|
|
@@ -903,6 +925,10 @@ def request(
|
|
|
903
925
|
"""
|
|
904
926
|
Request testing from Testing Farm.
|
|
905
927
|
"""
|
|
928
|
+
|
|
929
|
+
# Accept these arguments only via environment variables
|
|
930
|
+
check_unexpected_arguments(context, "api_url", "api_token")
|
|
931
|
+
|
|
906
932
|
# Split comma separated arches
|
|
907
933
|
arches = normalize_multistring_option(arches)
|
|
908
934
|
|
|
@@ -1038,6 +1064,8 @@ def request(
|
|
|
1038
1064
|
environment["hardware"] = hw_constraints(hardware)
|
|
1039
1065
|
|
|
1040
1066
|
if kickstart:
|
|
1067
|
+
# Typer escapes newlines in options, we need to unescape them
|
|
1068
|
+
kickstart = [codecs.decode(value, 'unicode_escape') for value in kickstart] # pyre-ignore[6]
|
|
1041
1069
|
environment["kickstart"] = options_to_dict("environment kickstart", kickstart)
|
|
1042
1070
|
|
|
1043
1071
|
if redhat_brew_build:
|
|
@@ -1204,7 +1232,7 @@ def request(
|
|
|
1204
1232
|
request_id = response.json()['id']
|
|
1205
1233
|
|
|
1206
1234
|
# Watch the request and handle reservation
|
|
1207
|
-
watch(api_url, request_id, no_wait, reserve=reserve, autoconnect=autoconnect, format=WatchFormat.text)
|
|
1235
|
+
watch(context, api_url, request_id, no_wait, reserve=reserve, autoconnect=autoconnect, format=WatchFormat.text)
|
|
1208
1236
|
|
|
1209
1237
|
|
|
1210
1238
|
def restart(
|
|
@@ -1256,6 +1284,9 @@ def restart(
|
|
|
1256
1284
|
Just pass a request ID or an URL with a request ID to restart it.
|
|
1257
1285
|
"""
|
|
1258
1286
|
|
|
1287
|
+
# Accept these arguments only via environment variables
|
|
1288
|
+
check_unexpected_arguments(context, "api_url", "api_token", "internal_api_url")
|
|
1289
|
+
|
|
1259
1290
|
# UUID pattern
|
|
1260
1291
|
uuid_pattern = re.compile('[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}')
|
|
1261
1292
|
|
|
@@ -1473,11 +1504,18 @@ def restart(
|
|
|
1473
1504
|
|
|
1474
1505
|
# watch
|
|
1475
1506
|
watch(
|
|
1476
|
-
|
|
1507
|
+
context,
|
|
1508
|
+
str(api_url),
|
|
1509
|
+
response.json()['id'],
|
|
1510
|
+
no_wait,
|
|
1511
|
+
reserve=reserve,
|
|
1512
|
+
autoconnect=autoconnect,
|
|
1513
|
+
format=WatchFormat.text,
|
|
1477
1514
|
)
|
|
1478
1515
|
|
|
1479
1516
|
|
|
1480
1517
|
def run(
|
|
1518
|
+
context: typer.Context,
|
|
1481
1519
|
arch: str = typer.Option("x86_64", "--arch", help="Hardware platform of the target machine."),
|
|
1482
1520
|
compose: Optional[str] = typer.Option(
|
|
1483
1521
|
None,
|
|
@@ -1489,6 +1527,10 @@ def run(
|
|
|
1489
1527
|
secrets: Optional[List[str]] = OPTION_SECRETS,
|
|
1490
1528
|
dry_run: bool = OPTION_DRY_RUN,
|
|
1491
1529
|
verbose: bool = typer.Option(False, help="Be verbose."),
|
|
1530
|
+
# NOTE: we cannot use ARGUMENT_API_* because it would collide with command,
|
|
1531
|
+
# so use rather OPTION variants for this command
|
|
1532
|
+
api_url: str = OPTION_API_URL,
|
|
1533
|
+
api_token: str = OPTION_API_TOKEN,
|
|
1492
1534
|
command: List[str] = typer.Argument(..., help="Command to run. Use `--` to separate COMMAND from CLI options."),
|
|
1493
1535
|
):
|
|
1494
1536
|
"""
|
|
@@ -1496,7 +1538,7 @@ def run(
|
|
|
1496
1538
|
"""
|
|
1497
1539
|
|
|
1498
1540
|
# check for token
|
|
1499
|
-
if not
|
|
1541
|
+
if not api_token:
|
|
1500
1542
|
exit_error("No API token found, export `TESTING_FARM_API_TOKEN` environment variable.")
|
|
1501
1543
|
|
|
1502
1544
|
# create request
|
|
@@ -1530,7 +1572,7 @@ def run(
|
|
|
1530
1572
|
request["environments"] = [environment]
|
|
1531
1573
|
|
|
1532
1574
|
# submit request to Testing Farm
|
|
1533
|
-
post_url = urllib.parse.urljoin(
|
|
1575
|
+
post_url = urllib.parse.urljoin(api_url, "v0.1/requests")
|
|
1534
1576
|
|
|
1535
1577
|
# Setting up retries
|
|
1536
1578
|
session = requests.Session()
|
|
@@ -1544,7 +1586,7 @@ def run(
|
|
|
1544
1586
|
raise typer.Exit()
|
|
1545
1587
|
|
|
1546
1588
|
# handle errors
|
|
1547
|
-
response = session.post(post_url, json=request, headers=_get_headers(
|
|
1589
|
+
response = session.post(post_url, json=request, headers=_get_headers(api_token))
|
|
1548
1590
|
if response.status_code == 401:
|
|
1549
1591
|
exit_error(f"API token is invalid. See {settings.ONBOARDING_DOCS} for more information.")
|
|
1550
1592
|
|
|
@@ -1556,7 +1598,7 @@ def run(
|
|
|
1556
1598
|
exit_error(f"Unexpected error. Please file an issue to {settings.ISSUE_TRACKER}.")
|
|
1557
1599
|
|
|
1558
1600
|
id = response.json()['id']
|
|
1559
|
-
get_url = urllib.parse.urljoin(
|
|
1601
|
+
get_url = urllib.parse.urljoin(api_url, f"/v0.1/requests/{id}")
|
|
1560
1602
|
|
|
1561
1603
|
if verbose:
|
|
1562
1604
|
console.print(f"🔎 api [blue]{get_url}[/blue]")
|
|
@@ -1640,6 +1682,9 @@ def run(
|
|
|
1640
1682
|
|
|
1641
1683
|
|
|
1642
1684
|
def reserve(
|
|
1685
|
+
context: typer.Context,
|
|
1686
|
+
api_url: str = ARGUMENT_API_URL,
|
|
1687
|
+
api_token: str = ARGUMENT_API_TOKEN,
|
|
1643
1688
|
ssh_public_keys: List[str] = _option_ssh_public_keys(RESERVE_PANEL_GENERAL),
|
|
1644
1689
|
reservation_duration: int = _option_reservation_duration(RESERVE_PANEL_GENERAL),
|
|
1645
1690
|
arch: str = typer.Option(
|
|
@@ -1659,6 +1704,9 @@ def reserve(
|
|
|
1659
1704
|
repository: List[str] = OPTION_REPOSITORY,
|
|
1660
1705
|
repository_file: List[str] = OPTION_REPOSITORY_FILE,
|
|
1661
1706
|
redhat_brew_build: List[str] = OPTION_REDHAT_BREW_BUILD,
|
|
1707
|
+
tmt_discover: Optional[List[str]] = _generate_tmt_extra_args("discover"),
|
|
1708
|
+
tmt_prepare: Optional[List[str]] = _generate_tmt_extra_args("prepare"),
|
|
1709
|
+
tmt_finish: Optional[List[str]] = _generate_tmt_extra_args("finish"),
|
|
1662
1710
|
dry_run: bool = OPTION_DRY_RUN,
|
|
1663
1711
|
post_install_script: Optional[str] = OPTION_POST_INSTALL_SCRIPT,
|
|
1664
1712
|
print_only_request_id: bool = typer.Option(
|
|
@@ -1688,6 +1736,9 @@ def reserve(
|
|
|
1688
1736
|
|
|
1689
1737
|
_sanity_reserve()
|
|
1690
1738
|
|
|
1739
|
+
# Accept these arguments only via environment variables
|
|
1740
|
+
check_unexpected_arguments(context, "api_url", "api_token")
|
|
1741
|
+
|
|
1691
1742
|
# check for token
|
|
1692
1743
|
if not settings.API_TOKEN:
|
|
1693
1744
|
exit_error("No API token found, export `TESTING_FARM_API_TOKEN` environment variable.")
|
|
@@ -1734,6 +1785,8 @@ def reserve(
|
|
|
1734
1785
|
environment["settings"]["provisioning"]["tags"] = options_to_dict("tags", tags)
|
|
1735
1786
|
|
|
1736
1787
|
if kickstart:
|
|
1788
|
+
# Typer escapes newlines in options, we need to unescape them
|
|
1789
|
+
kickstart = [codecs.decode(value, 'unicode_escape') for value in kickstart] # pyre-ignore[6]
|
|
1737
1790
|
environment["kickstart"] = options_to_dict("environment kickstart", kickstart)
|
|
1738
1791
|
|
|
1739
1792
|
if redhat_brew_build:
|
|
@@ -1754,6 +1807,18 @@ def reserve(
|
|
|
1754
1807
|
if post_install_script:
|
|
1755
1808
|
environment["settings"]["provisioning"]["post_install_script"] = post_install_script
|
|
1756
1809
|
|
|
1810
|
+
if tmt_discover or tmt_prepare or tmt_finish:
|
|
1811
|
+
environment["tmt"] = {"extra_args": {}}
|
|
1812
|
+
|
|
1813
|
+
if tmt_discover:
|
|
1814
|
+
environment["tmt"]["extra_args"]["discover"] = tmt_discover
|
|
1815
|
+
|
|
1816
|
+
if tmt_prepare:
|
|
1817
|
+
environment["tmt"]["extra_args"]["prepare"] = tmt_prepare
|
|
1818
|
+
|
|
1819
|
+
if tmt_finish:
|
|
1820
|
+
environment["tmt"]["extra_args"]["finish"] = tmt_finish
|
|
1821
|
+
|
|
1757
1822
|
# Setting up retries
|
|
1758
1823
|
session = requests.Session()
|
|
1759
1824
|
install_http_retries(session)
|
|
@@ -1803,7 +1868,7 @@ def reserve(
|
|
|
1803
1868
|
console.print(f"⏳ Maximum reservation time is {DEFAULT_PIPELINE_TIMEOUT} minutes")
|
|
1804
1869
|
|
|
1805
1870
|
# submit request to Testing Farm
|
|
1806
|
-
post_url = urllib.parse.urljoin(
|
|
1871
|
+
post_url = urllib.parse.urljoin(api_url, "v0.1/requests")
|
|
1807
1872
|
|
|
1808
1873
|
# dry run
|
|
1809
1874
|
if dry_run:
|
|
@@ -1815,7 +1880,7 @@ def reserve(
|
|
|
1815
1880
|
raise typer.Exit()
|
|
1816
1881
|
|
|
1817
1882
|
# handle errors
|
|
1818
|
-
response = session.post(post_url, json=request, headers=_get_headers(
|
|
1883
|
+
response = session.post(post_url, json=request, headers=_get_headers(api_token))
|
|
1819
1884
|
if response.status_code == 401:
|
|
1820
1885
|
exit_error(f"API token is invalid. See {settings.ONBOARDING_DOCS} for more information.")
|
|
1821
1886
|
|
|
@@ -1830,7 +1895,7 @@ def reserve(
|
|
|
1830
1895
|
exit_error(f"Unexpected error. Please file an issue to {settings.ISSUE_TRACKER}.")
|
|
1831
1896
|
|
|
1832
1897
|
id = response.json()['id']
|
|
1833
|
-
get_url = urllib.parse.urljoin(
|
|
1898
|
+
get_url = urllib.parse.urljoin(api_url, f"/v0.1/requests/{id}")
|
|
1834
1899
|
|
|
1835
1900
|
if not print_only_request_id:
|
|
1836
1901
|
console.print(f"🔎 [blue]{get_url}[/blue]")
|
|
@@ -1955,7 +2020,7 @@ def reserve(
|
|
|
1955
2020
|
console.print(f"🌎 ssh root@{guest}")
|
|
1956
2021
|
|
|
1957
2022
|
if autoconnect:
|
|
1958
|
-
os.system(f"
|
|
2023
|
+
os.system(f"{SSH_RESERVATION_OPTIONS} root@{guest}") # noqa: E501
|
|
1959
2024
|
|
|
1960
2025
|
|
|
1961
2026
|
def update():
|
|
@@ -1967,6 +2032,7 @@ def update():
|
|
|
1967
2032
|
|
|
1968
2033
|
|
|
1969
2034
|
def cancel(
|
|
2035
|
+
context: typer.Context,
|
|
1970
2036
|
request_id: str = typer.Argument(
|
|
1971
2037
|
..., help="Testing Farm request to cancel. Specified by a request ID or a string containing it."
|
|
1972
2038
|
),
|
|
@@ -1977,6 +2043,9 @@ def cancel(
|
|
|
1977
2043
|
Cancel a Testing Farm request.
|
|
1978
2044
|
"""
|
|
1979
2045
|
|
|
2046
|
+
# Accept these arguments only via environment variables
|
|
2047
|
+
check_unexpected_arguments(context, "api_url", "api_token")
|
|
2048
|
+
|
|
1980
2049
|
# UUID pattern
|
|
1981
2050
|
uuid_pattern = re.compile('[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}')
|
|
1982
2051
|
|
|
@@ -2023,6 +2092,7 @@ def cancel(
|
|
|
2023
2092
|
|
|
2024
2093
|
|
|
2025
2094
|
def encrypt(
|
|
2095
|
+
context: typer.Context,
|
|
2026
2096
|
message: str = typer.Argument(..., help="Message to be encrypted."),
|
|
2027
2097
|
api_url: str = ARGUMENT_API_URL,
|
|
2028
2098
|
api_token: str = ARGUMENT_API_TOKEN,
|
|
@@ -2040,6 +2110,9 @@ def encrypt(
|
|
|
2040
2110
|
Create secrets for use in in-repository configuration.
|
|
2041
2111
|
"""
|
|
2042
2112
|
|
|
2113
|
+
# Accept these arguments only via environment variables
|
|
2114
|
+
check_unexpected_arguments(context, "api_url", "api_token")
|
|
2115
|
+
|
|
2043
2116
|
# check for token
|
|
2044
2117
|
if not api_token:
|
|
2045
2118
|
exit_error("No API token found, export `TESTING_FARM_API_TOKEN` environment variable")
|
|
@@ -13,6 +13,7 @@ from typing import Any, Dict, List, NoReturn, Optional, Union
|
|
|
13
13
|
import requests
|
|
14
14
|
import requests.adapters
|
|
15
15
|
import typer
|
|
16
|
+
from click.core import ParameterSource # pyre-ignore[21]
|
|
16
17
|
from rich.console import Console
|
|
17
18
|
from ruamel.yaml import YAML
|
|
18
19
|
from urllib3 import Retry
|
|
@@ -89,6 +90,20 @@ def hw_constraints(hardware: List[str]) -> Dict[Any, Any]:
|
|
|
89
90
|
constraints[first_key].append(new_dict)
|
|
90
91
|
continue
|
|
91
92
|
|
|
93
|
+
# Special handling for CPU flags as they are also a list
|
|
94
|
+
if first_key == 'cpu' and len(path_splitted) == 2 and path_splitted[1] == 'flag':
|
|
95
|
+
second_key = 'flag'
|
|
96
|
+
|
|
97
|
+
if first_key not in constraints:
|
|
98
|
+
constraints[first_key] = {}
|
|
99
|
+
|
|
100
|
+
if second_key not in constraints[first_key]:
|
|
101
|
+
constraints[first_key][second_key] = []
|
|
102
|
+
|
|
103
|
+
constraints[first_key][second_key].append(value)
|
|
104
|
+
|
|
105
|
+
continue
|
|
106
|
+
|
|
92
107
|
# Walk the path, step by step, and initialize containers along the way. The last step is not
|
|
93
108
|
# a name of another nested container, but actually a name in the last container.
|
|
94
109
|
container: Any = constraints
|
|
@@ -244,3 +259,12 @@ def read_glob_paths(glob_paths: List[str]) -> str:
|
|
|
244
259
|
contents.append(file.read())
|
|
245
260
|
|
|
246
261
|
return ''.join(contents)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def check_unexpected_arguments(context: typer.Context, *args: str) -> Union[None, NoReturn]:
|
|
265
|
+
for argument in args:
|
|
266
|
+
if context.get_parameter_source(argument) == ParameterSource.COMMANDLINE: # pyre-ignore[16]
|
|
267
|
+
exit_error(
|
|
268
|
+
f"Unexpected argument '{context.params.get(argument)}'. "
|
|
269
|
+
"Please make sure you are passing the parameters correctly."
|
|
270
|
+
)
|