devstack-cli 11.0.86__py3-none-any.whl → 11.0.88__py3-none-any.whl
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.
- cli.py +41 -50
- {devstack_cli-11.0.86.dist-info → devstack_cli-11.0.88.dist-info}/METADATA +1 -1
- devstack_cli-11.0.88.dist-info/RECORD +9 -0
- version.py +3 -3
- devstack_cli-11.0.86.dist-info/RECORD +0 -9
- {devstack_cli-11.0.86.dist-info → devstack_cli-11.0.88.dist-info}/LICENSE +0 -0
- {devstack_cli-11.0.86.dist-info → devstack_cli-11.0.88.dist-info}/WHEEL +0 -0
- {devstack_cli-11.0.86.dist-info → devstack_cli-11.0.88.dist-info}/entry_points.txt +0 -0
- {devstack_cli-11.0.86.dist-info → devstack_cli-11.0.88.dist-info}/top_level.txt +0 -0
cli.py
CHANGED
@@ -57,6 +57,30 @@ logger.addHandler(rich.logging.RichHandler())
|
|
57
57
|
json_logger = logging.getLogger('cli-json')
|
58
58
|
json_logger.addHandler(rich.logging.RichHandler(highlighter=rich.highlighter.JSONHighlighter()))
|
59
59
|
|
60
|
+
def is_valid_username(username: str) -> bool:
|
61
|
+
return re.match(r'^[a-zA-Z0-9]+[a-zA-Z0-9_.-]*$', username) is not None
|
62
|
+
|
63
|
+
def is_valid_hostname(hostname: str) -> bool:
|
64
|
+
return re.match(r'^([a-zA-Z0-9][a-zA-Z0-9-]*\.)*([a-zA-Z0-9][a-zA-Z0-9-]*)$', hostname) is not None
|
65
|
+
|
66
|
+
def is_valid_path(path: str) -> bool:
|
67
|
+
return re.match(r'^[a-zA-Z0-9/_.,\-+ ]+$', path) is not None
|
68
|
+
|
69
|
+
def ensure_valid_username(username: str) -> str:
|
70
|
+
if not is_valid_username(username):
|
71
|
+
raise ValueError(f'Invalid username: "{username}". Username must start with an alphanumeric character, and contain only alphanumeric characters, dots, underscores and hyphens.')
|
72
|
+
return username
|
73
|
+
|
74
|
+
def ensure_valid_hostname(hostname: str) -> str:
|
75
|
+
if not is_valid_hostname(hostname):
|
76
|
+
raise ValueError(f'Invalid hostname: "{hostname}". Hostname must start with an alphanumeric character, and contain only alphanumeric characters, dots, and hyphens.')
|
77
|
+
return hostname
|
78
|
+
|
79
|
+
def ensure_valid_path(path: str) -> str:
|
80
|
+
if not is_valid_path(path):
|
81
|
+
raise ValueError(f'Invalid path: "{path}". Path must start with an alphanumeric character, and contain only alphanumeric characters, slashes, dots, underscores, commas, pluses, hyphens, and spaces.')
|
82
|
+
return path
|
83
|
+
|
60
84
|
class SubprocessError(Exception):
|
61
85
|
"""A subprocess call returned with non-zero."""
|
62
86
|
|
@@ -1223,12 +1247,8 @@ class Cli:
|
|
1223
1247
|
self.port_forwarding_task = None
|
1224
1248
|
|
1225
1249
|
async def _bg_port_forwarding(self: 'Cli') -> None:
|
1226
|
-
remote_username = self.cde_type['value']['remote-username']
|
1227
|
-
|
1228
|
-
raise Exception(f'Invalid remote username: "{remote_username}". Only alphanumeric characters are allowed.')
|
1229
|
-
hostname = self.hostname
|
1230
|
-
if re.match(r'[^a-zA-Z0-9]', hostname):
|
1231
|
-
raise Exception(f'Invalid hostname: "{hostname}". Only alphanumeric characters are allowed.')
|
1250
|
+
remote_username = ensure_valid_username(self.cde_type['value']['remote-username'])
|
1251
|
+
hostname = ensure_valid_hostname(self.hostname)
|
1232
1252
|
service_ports = self.cde_type['value'].get('service-ports')
|
1233
1253
|
if service_ports is None:
|
1234
1254
|
service_ports = [
|
@@ -1247,9 +1267,9 @@ class Cli:
|
|
1247
1267
|
]
|
1248
1268
|
for port in service_ports:
|
1249
1269
|
if port[0] < 1 or port[0] > 65535:
|
1250
|
-
raise
|
1270
|
+
raise ValueError(f'Invalid port: "{port[0]}". Only numbers between 1 and 65535 are allowed.')
|
1251
1271
|
if port[1] < 1 or port[1] > 65535:
|
1252
|
-
raise
|
1272
|
+
raise ValueError(f'Invalid port: "{port[1]}". Only numbers between 1 and 65535 are allowed.')
|
1253
1273
|
while True:
|
1254
1274
|
logger.info('Starting port forwarding of %s', ', '.join(str(port[0]) for port in service_ports))
|
1255
1275
|
try:
|
@@ -1439,15 +1459,9 @@ class Cli:
|
|
1439
1459
|
return await self._process_remote_item_copy_file(file_info.filename)
|
1440
1460
|
|
1441
1461
|
async def _process_remote_item_copy_dir(self: 'Cli', filename: str) -> str:
|
1442
|
-
remote_username = self.cde_type['value']['remote-username']
|
1443
|
-
|
1444
|
-
|
1445
|
-
hostname = self.hostname
|
1446
|
-
if re.match(r'[^a-zA-Z0-9]', hostname):
|
1447
|
-
raise Exception(f'Invalid hostname: "{hostname}". Only alphanumeric characters are allowed.')
|
1448
|
-
remote_source_directory = self.cde_type['value']['remote-source-directory']
|
1449
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_source_directory):
|
1450
|
-
raise Exception(f'Invalid remote source directory: "{remote_source_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1462
|
+
remote_username = ensure_valid_username(self.cde_type['value']['remote-username'])
|
1463
|
+
hostname = ensure_valid_hostname(self.hostname)
|
1464
|
+
remote_source_directory = ensure_valid_path(self.cde_type['value']['remote-source-directory'])
|
1451
1465
|
await run_subprocess(
|
1452
1466
|
'rsync',
|
1453
1467
|
[
|
@@ -1462,9 +1476,7 @@ class Cli:
|
|
1462
1476
|
return f'Copied directory "{filename}"'
|
1463
1477
|
|
1464
1478
|
async def _process_remote_item_copy_file(self: 'Cli', filename: str) -> str:
|
1465
|
-
remote_source_directory = self.cde_type['value']['remote-source-directory']
|
1466
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_source_directory):
|
1467
|
-
raise Exception(f'Invalid remote source directory: "{remote_source_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1479
|
+
remote_source_directory = ensure_valid_path(self.cde_type['value']['remote-source-directory'])
|
1468
1480
|
await self.loop.run_in_executor(
|
1469
1481
|
executor=None,
|
1470
1482
|
func=functools.partial(
|
@@ -1476,15 +1488,9 @@ class Cli:
|
|
1476
1488
|
return f'Copied file "{filename}"'
|
1477
1489
|
|
1478
1490
|
async def _process_remote_item_clone(self: 'Cli', filename: str) -> str:
|
1479
|
-
remote_username = self.cde_type['value']['remote-username']
|
1480
|
-
|
1481
|
-
|
1482
|
-
hostname = self.hostname
|
1483
|
-
if re.match(r'[^a-zA-Z0-9]', hostname):
|
1484
|
-
raise Exception(f'Invalid hostname: "{hostname}". Only alphanumeric characters are allowed.')
|
1485
|
-
remote_source_directory = self.cde_type['value']['remote-source-directory']
|
1486
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_source_directory):
|
1487
|
-
raise Exception(f'Invalid remote source directory: "{remote_source_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1491
|
+
remote_username = ensure_valid_username(self.cde_type['value']['remote-username'])
|
1492
|
+
hostname = ensure_valid_hostname(self.hostname)
|
1493
|
+
remote_source_directory = ensure_valid_path(self.cde_type['value']['remote-source-directory'])
|
1488
1494
|
await run_subprocess(
|
1489
1495
|
'git',
|
1490
1496
|
[
|
@@ -1530,15 +1536,9 @@ class Cli:
|
|
1530
1536
|
return f'Cloned repository "{filename}"'
|
1531
1537
|
|
1532
1538
|
async def _background_sync(self: 'Cli') -> None:
|
1533
|
-
remote_username = self.cde_type['value']['remote-username']
|
1534
|
-
|
1535
|
-
|
1536
|
-
hostname = self.hostname
|
1537
|
-
if re.match(r'[^a-zA-Z0-9]', hostname):
|
1538
|
-
raise Exception(f'Invalid hostname: "{hostname}". Only alphanumeric characters are allowed.')
|
1539
|
-
remote_source_directory = self.cde_type['value']['remote-source-directory']
|
1540
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_source_directory):
|
1541
|
-
raise Exception(f'Invalid remote source directory: "{remote_source_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1539
|
+
remote_username = ensure_valid_username(self.cde_type['value']['remote-username'])
|
1540
|
+
hostname = ensure_valid_hostname(self.hostname)
|
1541
|
+
remote_source_directory = ensure_valid_path(self.cde_type['value']['remote-source-directory'])
|
1542
1542
|
logger.debug('Starting background sync')
|
1543
1543
|
self.local_source_directory.mkdir(parents=True, exist_ok=True)
|
1544
1544
|
with contextlib.suppress(OSError):
|
@@ -1606,19 +1606,10 @@ class Cli:
|
|
1606
1606
|
logger.info('Background sync done')
|
1607
1607
|
|
1608
1608
|
async def _reverse_background_sync(self: 'Cli') -> None:
|
1609
|
-
remote_username = self.cde_type['value']['remote-username']
|
1610
|
-
|
1611
|
-
|
1612
|
-
hostname = self.hostname
|
1613
|
-
if re.match(r'[^a-zA-Z0-9]', hostname):
|
1614
|
-
raise Exception(f'Invalid hostname: "{hostname}". Only alphanumeric characters are allowed.')
|
1615
|
-
remote_source_directory = self.cde_type['value']['remote-source-directory']
|
1616
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_source_directory):
|
1617
|
-
raise Exception(f'Invalid remote source directory: "{remote_source_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1609
|
+
remote_username = ensure_valid_username(self.cde_type['value']['remote-username'])
|
1610
|
+
hostname = ensure_valid_hostname(self.hostname)
|
1611
|
+
remote_output_directory = ensure_valid_path(self.cde_type['value']['remote-output-directory'])
|
1618
1612
|
logger.debug('Starting reverse background sync')
|
1619
|
-
remote_output_directory = self.cde_type['value']['remote-output-directory']
|
1620
|
-
if re.match(r'[^a-zA-Z0-9/_.-]', remote_output_directory):
|
1621
|
-
raise Exception(f'Invalid remote output directory: "{remote_output_directory}". Only alphanumeric characters, slashes, underscores, dots and hyphens are allowed.')
|
1622
1613
|
with contextlib.suppress(OSError):
|
1623
1614
|
self.sftp_client.mkdir(remote_output_directory)
|
1624
1615
|
self.local_output_directory.mkdir(parents=True, exist_ok=True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: devstack-cli
|
3
|
-
Version: 11.0.
|
3
|
+
Version: 11.0.88
|
4
4
|
Summary: Command-line access to Cloud Development Environments (CDEs) created by Cloudomation DevStack
|
5
5
|
Author-email: Stefan Mückstein <stefan@cloudomation.com>
|
6
6
|
Project-URL: Homepage, https://cloudomation.com/
|
@@ -0,0 +1,9 @@
|
|
1
|
+
__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
cli.py,sha256=-KSbyW0gGMp18u3EkQ2J6H4tiVyyW__0zUDSiC1J75o,81433
|
3
|
+
version.py,sha256=qza2HnAKjrjiL9HnU7qLhuqYTAtgntUo6i3QbWeRuDU,180
|
4
|
+
devstack_cli-11.0.88.dist-info/LICENSE,sha256=OBXZbEUMtIHIzyISkJ9fJlf_imds3rcKqeQu9yiyUJI,1055
|
5
|
+
devstack_cli-11.0.88.dist-info/METADATA,sha256=temVYDs588G-T9yMi7_P4q3tMObOMATNstdm6reeeFw,4287
|
6
|
+
devstack_cli-11.0.88.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
7
|
+
devstack_cli-11.0.88.dist-info/entry_points.txt,sha256=f0xb4DIk0a7E5kyZ7YpoLhtjoagQj5VQpeBbW9a8A9Y,42
|
8
|
+
devstack_cli-11.0.88.dist-info/top_level.txt,sha256=lP8zvU46Am_G0MPcNmCI6f0sMfwpDUWpTROaPs-IEPk,21
|
9
|
+
devstack_cli-11.0.88.dist-info/RECORD,,
|
version.py
CHANGED
@@ -4,6 +4,6 @@ constants, set by build
|
|
4
4
|
|
5
5
|
MAJOR = '11'
|
6
6
|
BRANCH_NAME = 'release-11'
|
7
|
-
BUILD_DATE = '2025-02-
|
8
|
-
SHORT_SHA = '
|
9
|
-
VERSION = '11+release-11.2025-02-
|
7
|
+
BUILD_DATE = '2025-02-14-040012'
|
8
|
+
SHORT_SHA = '0dab1e2'
|
9
|
+
VERSION = '11+release-11.2025-02-14-040012.0dab1e2'
|
@@ -1,9 +0,0 @@
|
|
1
|
-
__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
cli.py,sha256=MB5BGbhMwEs1icEsDpheBQDrNk1ck9QeDV2vTzYU82s,83037
|
3
|
-
version.py,sha256=sJpyKrc9U7z_DqGEbXgd2Wk7mEkpNR377tdh5lUi-hc,180
|
4
|
-
devstack_cli-11.0.86.dist-info/LICENSE,sha256=OBXZbEUMtIHIzyISkJ9fJlf_imds3rcKqeQu9yiyUJI,1055
|
5
|
-
devstack_cli-11.0.86.dist-info/METADATA,sha256=ms4yDvaRUZ9etdQhZfQ4bfF1H4fGoMRBQrwmfaCCUEw,4287
|
6
|
-
devstack_cli-11.0.86.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
7
|
-
devstack_cli-11.0.86.dist-info/entry_points.txt,sha256=f0xb4DIk0a7E5kyZ7YpoLhtjoagQj5VQpeBbW9a8A9Y,42
|
8
|
-
devstack_cli-11.0.86.dist-info/top_level.txt,sha256=lP8zvU46Am_G0MPcNmCI6f0sMfwpDUWpTROaPs-IEPk,21
|
9
|
-
devstack_cli-11.0.86.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|