Subcloud restore backup yaml error handling

This commit handles the exception raised when passing
an invalid YAML file to subcloud backup restore command.

Test Plan:

PASS: Provision a subcloud and create its backup.
Create an invalid backup YAML file for restore. Run
subcloud backup restore command and verify that the
error "Invalid type for <key>. Expected dict got
<value>." message is displayed.

PASS: Provision a subcloud. Run backup creation
command passing a int as value for all
parameters that expects string(subcloud, group,
sysadmin_password). Verify that the backup creation
fails with "Invalid type for <key>. Expected str got
int".

PASS: Provision a subcloud. Run backup creation
command passing a string as value for backup_values.
Verify that the backup creation fails with "Invalid
type for <key>. Expected dict got str".

PASS: Provision and backup a subcloud. Attempt
to delete its backup passing an int as parameters
values for --subcloud, --group, sysadmin_password.
Verify that the deletion fails with "Invalid type
for <key>. Expected str got int".

Closes-bug: 2097402

Change-Id: Ief35656a5517f9ed8260658f992a2e1239c8bfcb
Signed-off-by: Gustavo Pereira <gustavo.lyrapereira@windriver.com>
This commit is contained in:
Gustavo Pereira 2025-02-05 00:48:22 -03:00
parent 49448400cc
commit 5463c223af
2 changed files with 41 additions and 20 deletions

View File

@ -54,31 +54,31 @@ class SubcloudBackupController(object):
expected_params = dict() expected_params = dict()
if verb == "create": if verb == "create":
expected_params = { expected_params = {
"subcloud": "text", "subcloud": str,
"group": "text", "group": str,
"local_only": "text", "local_only": str,
"registry_images": "text", "registry_images": str,
"backup_values": "yaml", "backup_values": dict,
"sysadmin_password": "text", "sysadmin_password": str,
} }
elif verb == "delete": elif verb == "delete":
expected_params = { expected_params = {
"release": "text", "release": str,
"subcloud": "text", "subcloud": str,
"group": "text", "group": str,
"local_only": "text", "local_only": str,
"sysadmin_password": "text", "sysadmin_password": str,
} }
elif verb == "restore": elif verb == "restore":
expected_params = { expected_params = {
"with_install": "text", "with_install": str,
"release": "text", "release": str,
"local_only": "text", "local_only": str,
"registry_images": "text", "registry_images": str,
"sysadmin_password": "text", "sysadmin_password": str,
"restore_values": "text", "restore_values": dict,
"subcloud": "text", "subcloud": str,
"group": "text", "group": str,
} }
else: else:
pecan.abort(400, _("Unexpected verb received")) pecan.abort(400, _("Unexpected verb received"))
@ -86,9 +86,11 @@ class SubcloudBackupController(object):
content_type = request.headers.get("content-type") content_type = request.headers.get("content-type")
LOG.info("Request content-type: %s" % content_type) LOG.info("Request content-type: %s" % content_type)
if "multipart/form-data" in content_type.lower(): if "multipart/form-data" in content_type.lower():
return SubcloudBackupController._get_multipart_payload( return SubcloudBackupController._get_multipart_payload(
request, expected_params request, expected_params
) )
else: else:
return SubcloudBackupController._get_json_payload(request, expected_params) return SubcloudBackupController._get_json_payload(request, expected_params)
@ -110,6 +112,20 @@ class SubcloudBackupController(object):
LOG.info("Got an unexpected parameter in: %s" % payload) LOG.info("Got an unexpected parameter in: %s" % payload)
pecan.abort(400, _("Unexpected parameter received")) pecan.abort(400, _("Unexpected parameter received"))
for key, value in payload.items():
expected_type = expected_params[key]
if key == "sysadmin_password":
# Do nothing, let _validate_and_decode_sysadmin_password
# handle this case
continue
if not isinstance(value, expected_type):
_msg = (
f"Invalid type for {key}: Expected "
f"{expected_type.__name__}, got {type(value).__name__}"
)
pecan.abort(400, _msg)
return payload return payload
@staticmethod @staticmethod

View File

@ -185,8 +185,13 @@ class TestSubcloudBackupPost(BaseTestSubcloudBackupPost):
def test_post_fails_without_sysadmin_password_in_multipart_payload(self): def test_post_fails_without_sysadmin_password_in_multipart_payload(self):
"""Test post fails without sysadmin password in multipart payload""" """Test post fails without sysadmin password in multipart payload"""
self.params = {
"subcloud": str(self.subcloud.id),
}
fake_restore_values = json.dumps(FAKE_RESTORE_VALUES_VALID_IP).encode()
self.upload_files = [ self.upload_files = [
("subcloud", "fake_subcloud", json.dumps(self.subcloud.id).encode()), ("backup_values", "fake_backup_values", fake_restore_values)
] ]
self._update_subcloud() self._update_subcloud()