Skip to content

Commit 1c82dbd

Browse files
petrutlucian94Dany9966
authored andcommitted
Let argparse convert the args
We'll pass type=comma_separated_kv_to_dict when defining user script args, letting it do the parsing for us.
1 parent f353c17 commit 1c82dbd

5 files changed

Lines changed: 65 additions & 33 deletions

File tree

coriolisclient/cli/deployments.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def get_parser(self, prog_name):
186186
action='append',
187187
required=False,
188188
dest="global_scripts",
189+
type=cli_utils.comma_separated_kv_to_dict,
189190
help='A script that will run for a particular os_type. This '
190191
'option can be used multiple times. '
191192
'Use: linux=/path/to/script.sh or '
@@ -197,6 +198,7 @@ def get_parser(self, prog_name):
197198
action='append',
198199
required=False,
199200
dest="instance_scripts",
201+
type=cli_utils.comma_separated_kv_to_dict,
200202
help='A script that will run for a particular '
201203
'instance specified by the --instance option. '
202204
'This option can be used multiple times. '

coriolisclient/cli/transfers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,15 @@ def get_parser(self, prog_name):
198198
parser.add_argument('--user-script-global', action='append',
199199
required=False,
200200
dest="global_scripts",
201+
type=cli_utils.comma_separated_kv_to_dict,
201202
help='A script that will run for a particular '
202203
'os_type. This option can be used multiple '
203204
'times. Use: linux=/path/to/script.sh or '
204205
'windows=/path/to/script.ps1')
205206
parser.add_argument('--user-script-instance', action='append',
206207
required=False,
207208
dest="instance_scripts",
209+
type=cli_utils.comma_separated_kv_to_dict,
208210
help='A script that will run for a particular '
209211
'instance specified by the --instance option. '
210212
'This option can be used multiple times. '
@@ -361,13 +363,15 @@ def get_parser(self, prog_name):
361363
parser.add_argument('--user-script-global', action='append',
362364
required=False,
363365
dest="global_scripts",
366+
type=cli_utils.comma_separated_kv_to_dict,
364367
help='A script that will run for a particular '
365368
'os_type. This option can be used multiple '
366369
'times. Use: linux=/path/to/script.sh or '
367370
'windows=/path/to/script.ps1')
368371
parser.add_argument('--user-script-instance', action='append',
369372
required=False,
370373
dest="instance_scripts",
374+
type=cli_utils.comma_separated_kv_to_dict,
371375
help='A script that will run for a particular '
372376
'instance specified by the --instance option. '
373377
'This option can be used multiple times. '

coriolisclient/cli/utils.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -202,29 +202,39 @@ def comma_separated_kv_to_dict(input_string: str) -> dict:
202202
for kv_pair in kv_pairs:
203203
try:
204204
key, value = kv_pair.split("=")
205-
except ValueError:
206-
raise ValueError("Not a <key>=<value> pair: %s" % kv_pair)
205+
except ValueError as err:
206+
raise ValueError(
207+
"Not a <key>=<value> pair: %s. " % kv_pair) from err
207208
out[key] = value
208209
return out
209210

210211

211-
def compose_user_scripts(global_scripts, instance_scripts):
212+
def compose_user_scripts(
213+
global_scripts: list[dict],
214+
instance_scripts: list[dict],
215+
) -> dict:
216+
"""Process user script arguments.
217+
218+
:param global_scripts: a list of dicts describing user scripts, the target
219+
OS and the phase in which the scripts should be invoked.
220+
The dicts are expected to contain the following keys:
221+
* <os>: one of the operating systems supported by Coriolis,
222+
e.g. "windows" or "linux". The value will be a local script path.
223+
* "phase": optional phase, defaults to "osmorphing_post_os_mount".
224+
:param instance_scripts: a list of dicts describing user scripts, each
225+
having a corresponding instance.
226+
The dicts are expected to contain the following keys:
227+
* <instance>: The name of the instance where the script must run.
228+
* "phase": optional phase, defaults to "osmorphing_post_os_mount".
229+
:returns: the processed list of scripts as expected by the Coriolis API.
230+
"""
212231
ret = {
213232
"global": {},
214233
"instances": {}
215234
}
216235
global_scripts = global_scripts or []
217236
instance_scripts = instance_scripts or []
218-
for global_script_str_params in global_scripts:
219-
try:
220-
params = comma_separated_kv_to_dict(global_script_str_params)
221-
except ValueError:
222-
raise ValueError(
223-
"Invalid global user script parameter: %s. Expecting "
224-
"<os_type>=<script_path>. Can optionally include a comma "
225-
"separated phase parameter, "
226-
"e.g. <os_type>=<script_path>,phase=<phase>" %
227-
global_script_str_params)
237+
for params in global_scripts:
228238
phase = params.pop("phase", constants.PHASE_OSMORPHING_POST_OS_MOUNT)
229239
if phase not in constants.USER_SCRIPT_PHASES:
230240
raise ValueError(
@@ -264,17 +274,7 @@ def compose_user_scripts(global_scripts, instance_scripts):
264274
}
265275
ret["global"][os_type].append(script_entry)
266276

267-
for instance_scripts_str_params in instance_scripts:
268-
try:
269-
params = comma_separated_kv_to_dict(instance_scripts_str_params)
270-
except ValueError:
271-
raise ValueError(
272-
"Invalid instance user script parameter: %s. Expecting "
273-
"<instance>=<script_path>. Can optionally include a comma "
274-
"separated phase parameter, "
275-
"e.g. <instance>=<script_path>,phase=<phase>" %
276-
instance_scripts_str_params)
277-
277+
for params in instance_scripts:
278278
phase = params.pop("phase", constants.PHASE_OSMORPHING_POST_OS_MOUNT)
279279
if phase not in constants.USER_SCRIPT_PHASES:
280280
raise ValueError(

coriolisclient/tests/cli/test_deployments.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,16 @@ def test_get_parser(self):
210210
parser = self.cli.get_parser('coriolis')
211211
global_script = "linux=/linux/path"
212212
instance_script = "instance1=/instance1/path"
213+
214+
processed_global_scripts = [{"linux": "/linux/path"}]
215+
processed_instance_scripts = [{"instance1": "/instance1/path"}]
213216
args = parser.parse_args([
214217
'transfer_id', '--force', '--dont-clone-disks',
215218
'--skip-os-morphing', '--user-script-global', global_script,
216219
'--user-script-instance', instance_script])
217220
self.assertEqual(
218-
('transfer_id', True, False, True, [global_script],
219-
[instance_script]),
221+
('transfer_id', True, False, True,
222+
processed_global_scripts, processed_instance_scripts),
220223
(args.transfer, args.force, args.clone_disks,
221224
args.skip_os_morphing, args.global_scripts,
222225
args.instance_scripts))

coriolisclient/tests/cli/test_utils.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,17 @@ def test_get_option_value_from_args_json_value_error(self):
234234
"option-name"
235235
)
236236

237+
@ddt.data(
238+
"linux script",
239+
"linux=script,phase",
240+
)
241+
def test_comma_separated_kv_to_dict_invalid(self, params):
242+
self.assertRaises(
243+
ValueError,
244+
utils.comma_separated_kv_to_dict,
245+
params,
246+
)
247+
237248
@ddt.data(
238249
{
239250
"global_scripts": None,
@@ -253,11 +264,6 @@ def test_get_option_value_from_args_json_value_error(self):
253264
"instances": {"instance_1": None}
254265
}
255266
},
256-
{
257-
"global_scripts": ["linux script"],
258-
"instance_scripts": ["linux script"],
259-
"expected_result": None
260-
},
261267
{
262268
"global_scripts": ["invalid_os=scrips"],
263269
"instance_scripts": None,
@@ -289,8 +295,14 @@ def test_get_option_value_from_args_json_value_error(self):
289295
}
290296
)
291297
def test_compose_user_scripts(self, data):
292-
global_scripts = data["global_scripts"]
293-
instance_scripts = data["instance_scripts"]
298+
global_scripts = [
299+
utils.comma_separated_kv_to_dict(params)
300+
for params in data["global_scripts"]
301+
] if data["global_scripts"] else None
302+
instance_scripts = [
303+
utils.comma_separated_kv_to_dict(params)
304+
for params in data["instance_scripts"]
305+
] if data["instance_scripts"] else None
294306
expected_result = data["expected_result"]
295307

296308
if expected_result:
@@ -323,6 +335,17 @@ def test_compose_user_scripts_from_file(self):
323335
f"instance1={script_path},phase=osmorphing_pre_os_mount", # noqa
324336
]
325337

338+
# We could've provided the dicts directly but we'll exercise
339+
# comma_separated_kv_to_dict instead.
340+
global_scripts = [
341+
utils.comma_separated_kv_to_dict(params)
342+
for params in global_scripts
343+
]
344+
instance_scripts = [
345+
utils.comma_separated_kv_to_dict(params)
346+
for params in instance_scripts
347+
]
348+
326349
result = utils.compose_user_scripts(global_scripts, instance_scripts)
327350

328351
payload = '"mock_script1"\n"mock_script2"\n'

0 commit comments

Comments
 (0)