Create unit tests for the metadata validation logic
These changes organize and add more unit tests to the Validate_metadata_file function. Improvements implemented: - A separate test file was created to be exclusive to the app_metadata.py file. - Data input was organized in external files. - Unit tests added individually to each key in the yaml file. Test plan: PASS: Run tox py39, pylint and verify that they are all passing. PASS: The output of the 'tox -e cover' was improved from 13% to 58%. Within the same file there are other functions to be tested that are not the scope of this demand. Story: 2010929 Task: 49834 Change-Id: If4bdb734990582f302b1e0d20179e02c524de546 Signed-off-by: David Bastos <david.barbosabastos@windriver.com>
This commit is contained in:
parent
3773c65f61
commit
fa5855845c
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
# Copyright (c) 2023-2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
@ -125,7 +125,7 @@ def validate_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
if not error_message:
|
||||
error_message = _("Invalid boolean value: {}"
|
||||
.format(value))
|
||||
raise exception.SysinvException(error_message)
|
||||
raise exception.SysinvException(error_message)
|
||||
|
||||
def validate_dict(value, error_message=None):
|
||||
"""Validate dictionary types"""
|
||||
|
@ -142,7 +142,7 @@ def validate_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
if not isinstance(value, list):
|
||||
if not error_message:
|
||||
error_message = _("Invalid list: {}".format(value))
|
||||
raise exception.SysinvException(error_message)
|
||||
raise exception.SysinvException(error_message)
|
||||
|
||||
# Field-level validations:
|
||||
def validate_string_field(parent, key):
|
||||
|
@ -426,8 +426,9 @@ def validate_metadata_file(path, metadata_file, upgrade_from_release=None):
|
|||
six.string_types))
|
||||
for release, release_patches in supported_releases.items():
|
||||
validate_string(release, release_error_message)
|
||||
validate_list_field(release_patches,
|
||||
release_patches_error_message)
|
||||
validate_list(release_patches,
|
||||
release_patches_error_message)
|
||||
|
||||
for patch in release_patches:
|
||||
validate_string(patch, patch_error_message)
|
||||
if release == check_release:
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
behavior:
|
||||
platform_managed_app: true
|
||||
desired_state: applied
|
||||
evaluate_reapply:
|
||||
after:
|
||||
- metrics-server.1
|
||||
- vault.1
|
||||
- oran.3
|
||||
triggers:
|
||||
- type: kube-upgrade-complete
|
||||
filters:
|
||||
- availability: services-enabled
|
||||
- type: host-delete
|
||||
filters:
|
||||
- personality: controller
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
k8s_upgrades:
|
||||
auto_update: true
|
||||
timing: pre
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
maintain_user_overrides: true
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
maximum: 'v1.2.4'
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
maintain_user_overrides: true
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
repo: 'stx-platform'
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
supported_releases:
|
||||
TEST.SW.VERSION:
|
||||
- "patch_0001"
|
||||
- "patch_0002"
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
upgrades:
|
||||
auto_update: true
|
||||
update_failure_no_rollback: true
|
||||
from_version:
|
||||
- 1
|
||||
- 2
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
maintain_user_overrides: true
|
|
@ -0,0 +1,536 @@
|
|||
#
|
||||
# Copyright (c) 2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
"""
|
||||
Tests for app_metadata.py
|
||||
"""
|
||||
|
||||
import io
|
||||
import mock
|
||||
import os.path
|
||||
import testtools
|
||||
import yaml
|
||||
|
||||
from sysinv.common import app_metadata
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import exception
|
||||
|
||||
|
||||
# these unit tests do not need to subclass base.TestCase
|
||||
class Validate_metadata_file(testtools.TestCase):
|
||||
|
||||
def get_metadata_yaml_sample(self, yaml_file_name):
|
||||
"""Help Funcition to import example yaml
|
||||
|
||||
:param yaml_file_name: path to yaml
|
||||
"""
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
"data", yaml_file_name)
|
||||
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
yaml_content = f.read()
|
||||
|
||||
return yaml_content
|
||||
|
||||
def test_nofile(self):
|
||||
"""Verify results of validate_metadata_file
|
||||
|
||||
when if no file is found, returns:
|
||||
app_name = "", app_version = "", patches = []
|
||||
"""
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("invalid_path",
|
||||
"invalid_file",
|
||||
upgrade_from_release=None)
|
||||
# if the file is not loaded or has invalid contents
|
||||
# validate_metadata_file returns two empty strings and
|
||||
# an empty list ie: "","",[]
|
||||
self.assertEqual(app_name, "")
|
||||
self.assertEqual(app_version, "")
|
||||
self.assertEqual(patches, [])
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation(self, _mock_isfile, _mock_open):
|
||||
"""This test mocks file operations
|
||||
|
||||
Returns static file contents to allow unit
|
||||
testing the validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
yaml_content = self.get_metadata_yaml_sample('sample_metadata.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validate_with_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""This test mocks file operations with bad values
|
||||
|
||||
This test mocks file operations and verifies
|
||||
failure handling in how the yaml is validated
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# app_name cannot be None
|
||||
{constants.APP_METADATA_NAME: None},
|
||||
# app_version cannot be None
|
||||
{constants.APP_METADATA_VERSION: None},
|
||||
# minimum or maximum cannot be a boolean
|
||||
{constants.APP_METADATA_SUPPORTED_K8S_VERSION: {
|
||||
constants.APP_METADATA_MINIMUM: True}},
|
||||
# minimum or maximum cannot be a number
|
||||
{constants.APP_METADATA_SUPPORTED_K8S_VERSION: {
|
||||
constants.APP_METADATA_MINIMUM: 2}},
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample('sample_metadata.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_supported_k8s_version(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate supported_k8s_version
|
||||
|
||||
This test mocks file operations with supported_k8s_version key
|
||||
and returns static file contents to allow unit
|
||||
testing the validation code
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
yaml_content = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_k8s_versions.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_supported_k8s_version_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate supported_k8s_version with bad values
|
||||
|
||||
This test mocks file operations to supported_k8s_version key
|
||||
and verifies failure handling in how the yaml is validated
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# supported_k8s_version must be a dict
|
||||
{constants.APP_METADATA_SUPPORTED_K8S_VERSION: None},
|
||||
# minimum must be a string
|
||||
{constants.APP_METADATA_SUPPORTED_K8S_VERSION: {
|
||||
constants.APP_METADATA_MINIMUM: True}},
|
||||
# minimum must be a string
|
||||
{constants.APP_METADATA_SUPPORTED_K8S_VERSION: {
|
||||
constants.APP_METADATA_MAXIMUM: 1}}
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_k8s_versions.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_without_min_k8s_version(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate supported_k8s_version without supported_k8s_version
|
||||
|
||||
This test mocks file operations without supported_k8s_version
|
||||
key and returns static file contents to allow unit testing the
|
||||
validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
yaml_content = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_without_k8s_minimum_version.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_with_upgrade_key(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate upgrades key
|
||||
|
||||
This test mocks file operations with upgrades key
|
||||
and returns static file contents to allow unit testing
|
||||
the validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
yaml_content = self.get_metadata_yaml_sample('sample_metadata_upgrades.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_with_upgrade_key_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate upgrades key with bad values
|
||||
|
||||
This test mocks file operations to upgrades key and
|
||||
verifies failure handling in how the yaml is validated
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# upgrades must be a dict
|
||||
{constants.APP_METADATA_UPGRADES: None},
|
||||
# update_failure_no_rollback must be a boolean string like: <true/false/yes/no>
|
||||
{constants.APP_METADATA_UPGRADES: {
|
||||
constants.APP_METADATA_UPDATE_FAILURE_SKIP_RECOVERY: 123}},
|
||||
# auto_update must be a boolean string like: <true/false/yes/no>
|
||||
{constants.APP_METADATA_UPGRADES: {constants.APP_METADATA_AUTO_UPDATE: 123}},
|
||||
# auto_update must be a list
|
||||
{constants.APP_METADATA_UPGRADES: {constants.APP_METADATA_FROM_VERSIONS: {}}}
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample('sample_metadata_upgrades.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_repo_key(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate repo key
|
||||
|
||||
This test mocks file operations with repo key and
|
||||
returns static file contents to allow unit testing
|
||||
the validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
yaml_content = self.get_metadata_yaml_sample('sample_metadata_repo.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_behavior_key(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate behavior key
|
||||
|
||||
This test mocks file operations with behavior key and
|
||||
returns static file contents to allow unit testing
|
||||
the validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
yaml_content = self.get_metadata_yaml_sample('sample_metadata_behavior.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validate_behavior_key_with_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate behavior key with bad values
|
||||
|
||||
This test mocks file operations to behavior key and
|
||||
verifies failure handling in how the yaml is validated
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# behavior must be a dict
|
||||
{constants.APP_METADATA_BEHAVIOR: []},
|
||||
# platform_managed_app must be a boolean string like: <true/false/yes/no>
|
||||
{constants.APP_METADATA_BEHAVIOR: {
|
||||
constants.APP_METADATA_PLATFORM_MANAGED_APP: "something"}},
|
||||
# desired_state must be a dict
|
||||
{constants.APP_METADATA_BEHAVIOR: {
|
||||
constants.APP_METADATA_DESIRED_STATE: 2}},
|
||||
# evaluate_reapply must be a dict
|
||||
{constants.APP_METADATA_BEHAVIOR: {constants.APP_METADATA_EVALUATE_REAPPLY: []}},
|
||||
# after must be a list
|
||||
{constants.APP_METADATA_BEHAVIOR: {constants.APP_METADATA_EVALUATE_REAPPLY: {
|
||||
constants.APP_METADATA_AFTER: {}}}},
|
||||
# triggers must be a list
|
||||
{constants.APP_METADATA_BEHAVIOR: {constants.APP_METADATA_EVALUATE_REAPPLY: {
|
||||
constants.APP_METADATA_TRIGGERS: {}}}},
|
||||
# type must be a string
|
||||
{constants.APP_METADATA_BEHAVIOR: {
|
||||
constants.APP_METADATA_EVALUATE_REAPPLY: {
|
||||
constants.APP_METADATA_TRIGGERS: [{constants.APP_METADATA_TYPE: 1}]
|
||||
}
|
||||
}},
|
||||
# filter_field must be a string
|
||||
{constants.APP_METADATA_BEHAVIOR: {
|
||||
constants.APP_METADATA_EVALUATE_REAPPLY: {
|
||||
constants.APP_METADATA_TRIGGERS: [{constants.APP_METADATA_FILTER_FIELD: 1}]
|
||||
}
|
||||
}},
|
||||
# filters must be a list
|
||||
{constants.APP_METADATA_BEHAVIOR: {
|
||||
constants.APP_METADATA_EVALUATE_REAPPLY: {
|
||||
constants.APP_METADATA_TRIGGERS: [{constants.APP_METADATA_FILTERS: {}}]
|
||||
}
|
||||
}}
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample('sample_metadata_behavior.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_supported_releases(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate supported_releases key
|
||||
|
||||
This test mocks file operations with supported_releases
|
||||
key and returns static file contents to allow unit testing
|
||||
the validation code
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
yaml_content = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_supported_releases.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
self.assertEqual(patches, ["patch_0001", "patch_0002"])
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_supported_releases_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate supported_releases key with bad values
|
||||
|
||||
This test mocks file operations to supported_releases key
|
||||
and verifies failure handling in how the yaml is validated
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# supported_releases must be a dict
|
||||
{constants.APP_METADATA_SUPPORTED_RELEASES: []},
|
||||
# releases group must be a list
|
||||
{constants.APP_METADATA_SUPPORTED_RELEASES: {"TEST.SW.VERSION": {}}},
|
||||
# releases item must be a string
|
||||
{constants.APP_METADATA_SUPPORTED_RELEASES: {"TEST.SW.VERSION": [1, True]}},
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_supported_releases.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_k8s_upgrades(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate k8s_upgrades key
|
||||
|
||||
This test mocks file operations with k8s_upgrades
|
||||
key and returns static file contents to allow unit testing
|
||||
the validation code
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
yaml_content = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_k8s_upgrades.yaml')
|
||||
_mock_open.return_value = io.StringIO(yaml_content)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_file_validation_k8s_upgrades_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""Validate k8s_upgrades key with bad values
|
||||
|
||||
This test mocks file operations to k8s_upgrades key
|
||||
and verifies failure handling in how the yaml is validated
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# k8s_upgrades must be a dict
|
||||
{constants.APP_METADATA_K8S_UPGRADES: []},
|
||||
# auto_update key must be a boolean string
|
||||
# like: <true/false/yes/no>
|
||||
{constants.APP_METADATA_K8S_UPGRADES: {
|
||||
constants.APP_METADATA_AUTO_UPDATE: None,
|
||||
}},
|
||||
{constants.APP_METADATA_K8S_UPGRADES: {
|
||||
constants.APP_METADATA_AUTO_UPDATE: True,
|
||||
# if auto_update key exist timing key must exist and be a string
|
||||
constants.APP_METADATA_TIMING: None
|
||||
}},
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = self.get_metadata_yaml_sample(
|
||||
'sample_metadata_k8s_upgrades.yaml')
|
||||
contents = yaml.safe_load(contents)
|
||||
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
|
@ -15,26 +15,22 @@
|
|||
# under the License.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2022 Wind River Systems, Inc.
|
||||
# Copyright (c) 2022,2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import errno
|
||||
import io
|
||||
import mock
|
||||
import netaddr
|
||||
import os
|
||||
import os.path
|
||||
import six.moves.builtins as __builtin__
|
||||
import tempfile
|
||||
import testtools
|
||||
import string
|
||||
import yaml
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from sysinv.common import app_metadata
|
||||
from sysinv.common import constants
|
||||
from sysinv.common import exception
|
||||
from sysinv.common import utils
|
||||
|
@ -448,131 +444,3 @@ class IntLikeTestCase(base.TestCase):
|
|||
self.assertFalse(
|
||||
utils.is_int_like("0cc3346e-9fef-4445-abe6-5d2b2690ec64"))
|
||||
self.assertFalse(utils.is_int_like("a1"))
|
||||
|
||||
|
||||
# these unit tests do not need to subclass base.TestCase
|
||||
class FindMetadataTestCase(testtools.TestCase):
|
||||
|
||||
sample_contents = """
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
helm_repo: stx-platform
|
||||
maintain_user_overrides: true
|
||||
supported_k8s_version:
|
||||
minimum: 'v1.2.3'
|
||||
maximum: 'v2.4.6'
|
||||
behavior:
|
||||
platform_managed_app: yes
|
||||
desired_state: applied
|
||||
evaluate_reapply:
|
||||
triggers:
|
||||
- type: runtime-apply-puppet # TODO(someuser): an inline comment
|
||||
- type: host-availability-updated
|
||||
- type: kube-upgrade-complete
|
||||
filters:
|
||||
- availability: services-enabled
|
||||
- type: host-delete
|
||||
filters:
|
||||
- personality: controller
|
||||
"""
|
||||
|
||||
bad_contents = """
|
||||
app_name: sample-app
|
||||
app_version: 1.2-3
|
||||
helm_repo: stx-platform
|
||||
maintain_user_overrides: true
|
||||
supported_k8s_version:
|
||||
minimum: 1 # must be a string, not a number
|
||||
maximum: true # must be a string, not a boolean
|
||||
behavior:
|
||||
platform_managed_app: yes
|
||||
desired_state: applied
|
||||
evaluate_reapply:
|
||||
triggers:
|
||||
- type: runtime-apply-puppet # TODO(someuser): an inline comment
|
||||
- type: host-availability-updated
|
||||
- type: kube-upgrade-complete
|
||||
filters:
|
||||
- availability: services-enabled
|
||||
- type: host-delete
|
||||
filters:
|
||||
- personality: controller
|
||||
"""
|
||||
|
||||
def test_validate_metadata_file_nofile(self):
|
||||
"""Verify results of validate_metadata_file
|
||||
|
||||
when if no file is found, returns:
|
||||
app_name = "", app_version = "", patches = []
|
||||
"""
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("invalid_path",
|
||||
"invalid_file",
|
||||
upgrade_from_release=None)
|
||||
# if the file is not loaded or has invalid contents
|
||||
# validate_metadata_file returns two empty strings and
|
||||
# an empty list ie: "","",[]
|
||||
self.assertEqual(app_name, "")
|
||||
self.assertEqual(app_version, "")
|
||||
self.assertEqual(patches, [])
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_validate_metadata_file(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""This test mocks file operations
|
||||
and returns static file contents to allow unit
|
||||
testing the validation code
|
||||
"""
|
||||
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# load fake yaml file contents for: sample-app 1.2-3
|
||||
_mock_open.return_value = io.StringIO(self.sample_contents)
|
||||
|
||||
app_name, app_version, patches = \
|
||||
app_metadata.validate_metadata_file("valid_path",
|
||||
"valid_file",
|
||||
upgrade_from_release=None)
|
||||
self.assertEqual(app_name, "sample-app")
|
||||
self.assertEqual(app_version, "1.2-3")
|
||||
|
||||
@mock.patch.object(io, 'open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_validate_metadata_file_bad_contents(self,
|
||||
_mock_isfile,
|
||||
_mock_open):
|
||||
"""This test mocks file operations and verifies
|
||||
failure handling in how the yaml is validated
|
||||
"""
|
||||
_mock_isfile.return_value = "True"
|
||||
|
||||
# bad_replacements is a list of atomic changes that
|
||||
# will trigger a SysinvException in the validator
|
||||
bad_replacements = [
|
||||
# app_name cannot be None
|
||||
{"app_name": None},
|
||||
# app_version cannot be None
|
||||
{"app_version": None},
|
||||
# behavior must be a dictionary (not a list)
|
||||
{"behavior": []},
|
||||
# minimum or maximum cannot be a boolean
|
||||
{"supported_k8s_version": {"minimum": True, "maximum": "2.4.6"}},
|
||||
# minimum or maximum cannot be a number
|
||||
{"supported_k8s_version": {"minimum": "1.2.3", "maximum": 2}},
|
||||
]
|
||||
|
||||
# start each loop with valid contents and replace
|
||||
# a certain section with bad contents so that the
|
||||
# validator will raise a SysinvException
|
||||
for bad_dict in bad_replacements:
|
||||
contents = yaml.safe_load(self.sample_contents)
|
||||
for key, value in bad_dict.items():
|
||||
contents[key] = value
|
||||
bad_contents = yaml.dump(contents)
|
||||
_mock_open.return_value = io.StringIO(bad_contents)
|
||||
self.assertRaises(exception.SysinvException,
|
||||
app_metadata.validate_metadata_file,
|
||||
"valid_path",
|
||||
"valid_file")
|
||||
|
|
Loading…
Reference in New Issue