def build(self, update_application=False, quiet=False): """ Create a virtual machine image of the configured host. Args: update_application (bool): If applicable, update the application definition Yaml file to use this image as host base for the selected provider. Warning, this will reset any yaml file formatting and comments. quiet (bool): If True, hide outputs. Returns: str: Image ID or path (Depending provider) """ manifest = self._packer.build(quiet=quiet) image = self._packer.get_artifact(manifest) if update_application and self._application_yaml: application = Application(self._application_yaml) try: section = application['package'][self._provider] except KeyError: section = application['package'][self._provider] = dict() section['type'] = 'vm_image' section['name'] = image application.save() return image
def test_web_service_integration(): """ Test web service integration. """ from accelpy._common import accelize_ws_session from accelpy._application import Application from random import randint # Use dev environment request_endpoint = accelize_ws_session._ENDPOINT accelize_ws_session._ENDPOINT = 'https://master.devmetering.accelize.com' product_id = 'accelize.com/accelpy/ci' version = f'{randint(0, 255)}.{randint(0, 255)}.{randint(0, 255)}' application = f'{product_id}:{version}' definition = dict(application=dict(product_id=product_id, type='container_service', version=version), fpga=dict(image='nothing'), package=dict(name='nothing', type='container_image'), accelize_drm=dict(use_service=False)) try: scr_app = Application(definition) # Test: push scr_app.push() # Test: Get srv_app = Application.from_id(application) assert scr_app._definition == srv_app._definition # Test: List assert product_id in Application.list() # Test: List with prefix assert product_id in Application.list('accelize.com/accelpy') # Test: List version assert version in Application.list_versions(product_id) # Test: List version with prefix assert version in Application.list_versions(product_id, version.split('.', 1)[0]) # Test: Delete srv_app.delete() assert version not in Application.list_versions(product_id) finally: try: srv_app.delete() except Exception: pass accelize_ws_session._endpoint = request_endpoint
def test_host_mocked(tmpdir): """ Test existing host provisioning on a mocked host. Args: tmpdir (py.path.local) tmpdir pytest fixture """ from os import environ from os.path import dirname, join import accelpy._host as accelpy_host from accelpy._host import Host from accelpy._application import Application environ['ACCELPY_DEBUG'] = 'True' mocked_app_path = join(dirname(__file__), 'host_mock.yml') app = join(dirname(__file__), 'host_app.yml') # Mock config dir accelpy_host_config_dir = accelpy_host.CONFIG_DIR config_dir = tmpdir.join('config').ensure(dir=True) accelpy_host.CONFIG_DIR = str(config_dir) # Mock user_config dir user_config = tmpdir.join('user_config').ensure(dir=True) # Mock an existing host with a virtual machine mocked_app = Application(mocked_app_path) mocked_provider = mocked_app.providers.pop() mocked_host = Host(name=get_name(mocked_app), application=mocked_app_path, provider=mocked_provider) try: # Create host mocked_host.apply(quiet=True) # Create the existing host Terraform configuration override user_config.join('host.user_override.tf').write_text('\n'.join( ('locals {', f' host_ip = ["{mocked_host.public_ip}"]', f' ssh_key_pem = "{mocked_host.ssh_private_key}"', f' remote_user = "******"', f' require_ask_pass = false', '}')), encoding='utf-8') # Provision existing host with Host(application=app, user_config=user_config, provider=f'host,{mocked_provider.replace(",", "-")}', destroy_on_exit=True, keep_config=False) as host: host.apply() # Restore mocked configuration finally: try: mocked_host.destroy(quiet=True, delete=True) finally: accelpy_host.CONFIG_DIR = accelpy_host_config_dir del environ['ACCELPY_DEBUG']
def _provider_completer(prefix, parsed_args, **_): """ Autocomplete "accelpy init --provider" Args: prefix (str): Provider prefix to filter. parsed_args (argparse.Namespace): CLI arguments. Returns: list of str: providers """ application = parsed_args.application if application is None: _completer_warn('Set "--application"/"-a" argument first to allow ' '"--provider"/"-p" argument autocompletion.') return # First try to get providers from cache from os.path import isfile, abspath from accelpy._common import get_cli_cache, set_cli_cache application = abspath(application) if isfile(application) else application cached = f'providers|{application}' providers = get_cli_cache(cached) # Else get providers from application and cache them if not providers: from accelpy._application import Application providers = Application(application).providers set_cli_cache(cached, list(providers)) # Filter with prefix return (provider for provider in providers if provider.startswith(prefix))
def parametrize_application_yml(argvalues, ids): """ Define parameters for testable application definition. Args: argvalues (list): Parametrize args values ids (list): Parametrize ID """ from accelpy._application import Application with scandir(dirname(realpath(__file__))) as entries: print(dirname(realpath(__file__))) for entry in entries: name, ext = splitext(entry.name) if ext == '.yml': print(entry.path) app = Application(entry.path) print(app, app.environments) if 'test' not in app._definition and not app.environments: continue name = name.split("_", 1)[1].replace('_', '-') for provider in app.environments: ids.append(f'{name}_{provider.replace(",", "-")}') argvalues.append(dict(path=entry.path, provider=provider))
def _action_push(args): """ Push application definition. Args: args (argparse.Namespace): CLI arguments. """ from accelpy._application import Application Application(args.file).push()
def _application(self): """ Application definition. Returns: accelpy._application.Application: Definition """ if not self._application_definition: self._application_definition = Application(self._application_yaml) return self._application_definition
def _get_product_ids(prefix): """ Get products IDs from web server. Args: prefix (str): Application prefix to filter. Returns: list of str: Product ids. """ from accelpy._application import Application return Application.list(prefix)
def _init_application_definition(self, application): """ Get remote or local application definition and save it locally. Args: application (str or path-like object): Application or path to application definition file. """ dst_path = join(self._config_dir, 'application.yml') # Try if application is a local path src_path = realpath(fsdecode(application)) # Link local definition file in configuration directory if isfile(src_path): return symlink(src_path, dst_path) # Lazy import: May not be used all time from accelpy._application import Application # Get application definition from accelize server Application.from_id(application).save(dst_path)
def _application(self): """ Application definition. Returns: accelpy._application.Application: Definition """ if not self._application_definition: # Lazy import: May not be used all time from accelpy._application import Application self._application_definition = Application( realpath(join(self._config_dir, 'application.yml'))) return self._application_definition
def _get_versions(prefix): """ Get versions from web server. Args: prefix (str): Application prefix to filter. Returns: list of str: Versions. """ from accelpy._application import Application product_id, version_prefix = prefix.split(':', 1) return ( f"{product_id}:{version}" for version in Application.list_versions(product_id, version_prefix))
def test_application(application_yml, tmpdir): """ Test applications based on their application definition. Args: application_yml (dict): Application detail. tmpdir (py.path.local) tmpdir pytest fixture. """ from os import environ from time import sleep, time from subprocess import run, STDOUT, PIPE import accelpy._host as accelpy_host from accelpy._host import Host from accelpy._application import Application yaml_path = application_yml['path'] provider = application_yml['provider'] environ['ACCELPY_DEBUG'] = 'True' # Mock config dir accelpy_host_config_dir = accelpy_host.CONFIG_DIR config_dir = tmpdir.join('config').ensure(dir=True) accelpy_host.CONFIG_DIR = str(config_dir) app = Application(yaml_path) # Tests try: with Host(application=yaml_path, provider=provider, name=get_name(app), destroy_on_exit=True, keep_config=False) as host: # Run Packer configuration check host._packer.validate() # Apply configuration host.apply() # Get test command command = app[provider]['test']['shell'] if not command: pytest.xfail('No test defined') # Run test else: # Evaluate "accelpy" shell variables for attr in dir(host): if attr.startswith('_'): continue shell_var = f'$(accelpy {attr})' if shell_var in command: command = command.replace(shell_var, getattr(host, attr)) # Run test command (With retries during 1 minute) print(f'\nRunning test command:\n{command.strip()}\n') timeout = time() + 60 while time() < timeout: result = run(command, universal_newlines=True, stderr=STDOUT, stdout=PIPE, shell=True) if not result.returncode: break sleep(1) print(f'\nTest command returned: ' f'{result.returncode}\n{result.stdout}') if result.returncode: pytest.fail(pytrace=False) # Restore mocked config dir finally: accelpy_host.CONFIG_DIR = accelpy_host_config_dir del environ['ACCELPY_DEBUG']
def test_apply(application_yml, tmpdir): """ Test applications based on their application definition. Args: application_yml (dict): Application detail. tmpdir (py.path.local) tmpdir pytest fixture """ from time import sleep from subprocess import run, STDOUT, PIPE import accelpy._host as accelpy_host from accelpy._host import Host from accelpy._application import Application yaml_path = application_yml['path'] provider = application_yml['provider'] # Mock config dir accelpy_host_config_dir = accelpy_host.CONFIG_DIR config_dir = tmpdir.join('config').ensure(dir=True) accelpy_host.CONFIG_DIR = str(config_dir) # Tests try: with Host(application=yaml_path, provider=provider, destroy_on_exit=True, keep_config=False) as host: # Apply configuration host.apply() # Get test command command = Application(yaml_path).get('test', 'shell', env=provider) # Evaluate "accelpy" shell variables for attr in dir(host): if attr.startswith('_'): continue shell_var = f'$(accelpy {attr})' if shell_var in command: command = command.replace(shell_var, getattr(host, attr)) # Run test command sleep(5) print(f'\nRunning test command:\n{command.strip()}\n') result = run(command, universal_newlines=True, stderr=STDOUT, stdout=PIPE, shell=True) print(f'\nTest command returned: ' f'{result.returncode}\n{result.stdout}') if result.returncode: pytest.fail(pytrace=False) # Restore mocked config dir finally: accelpy_host.CONFIG_DIR = accelpy_host_config_dir
def test_application(): """ Test common Application features. """ from json import loads from copy import deepcopy import accelpy._application as accelpy_app from accelpy._application import Application from accelpy.exceptions import RuntimeException # Test: Load from dict definition = { 'application': { 'product_id': 'my_product_id', 'version': '1.0.0', 'type': 'container_service' }, 'package': [{ 'type': 'container_image', 'name': 'my_container_image' }], 'fpga': { 'image': ['my_fpga_image'], 'count': 1 }, 'accelize_drm': { 'use_service': False } } app = Application(definition) # Test: __getitem__ assert app['application']['product_id'] == 'my_product_id' # Test: As dict assert app.to_dict()['application']['product_id'] == 'my_product_id' # Test: Cannot delete local application with pytest.raises(RuntimeException): app.delete() # Mock server accelpy_app_accelize_ws_session = accelpy_app.accelize_ws_session class Server: """Mocked server""" @staticmethod def request(path, *_, method='get', data=None, **__): """Mocked server response""" if '/productconfiguration/' in path: if method == 'get': srv_def = deepcopy(definition) srv_def['application']['configuration_id'] = 2 return dict(results=[srv_def]) elif method == 'post': assert loads(data) == definition, 'Pushed definition match' return {'application': {'configuration_id': 2}} elif method == 'delete': assert path.endswith('/2/') return elif ('/productconfigurationlistversion/' in path and method == 'get'): return dict(results=['1.0.0']) elif ('/productconfigurationlistproduct/' in path and method == 'get'): return dict(results=['product']) raise ValueError(f'path={path}; method={method}') accelpy_app.accelize_ws_session = Server # Test basic mocked server flow try: # Test: push Application(definition).push() # Test: Get assert Application.from_id('app').to_dict() == definition # Test: List assert Application.list() == ['product'] # Test: List version assert Application.list_versions('product') == ['1.0.0'] assert Application.list_versions('product', '1') == ['1.0.0'] # Test: Delete Application.from_id('app').delete() finally: accelpy_app.accelize_ws_session = accelpy_app_accelize_ws_session
def test_lint(tmpdir): """ Test application definition file lint Args: tmpdir (py.path.local) tmpdir pytest fixture """ from accelpy._application import Application, lint from accelpy.exceptions import ConfigurationException # Mock yaml definition file yml_file = tmpdir.join('application.yml') # Test: Load valid file yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image version: 1.0.0 # Override of value in environment my_provider: type: vm_image name: my_vm_image firewall_rules: - start_port: 1000 end_port: 1000 protocol: tcp direction: ingress - start_port: 1001 end_port: 1100 protocol: tcp direction: egress fpga: # Mandatory value only in environment my_provider: image: my_fpga_image """) app = Application(yml_file) # Test: __getitem__ assert app['application']['name'] == 'my_app' # Test section _node assert isinstance(app['firewall_rules'], list) assert isinstance(app['application'], dict) # Test: get assert app.get('fpga', 'image') is None assert app.get('fpga', 'image', 'my_provider') == ['my_fpga_image'] assert app.get('package', 'type') == 'container_image' assert app.get('package', 'type', 'my_provider') == 'vm_image' # Test: save app['package']['name'] = 'another_image' app.save() assert Application(yml_file)['package']['name'] == 'another_image' # Test: environment assert app.environments == {'my_provider'} # Test: Load valid file with missing not mandatory section yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: image """) app = Application(yml_file) assert app['firewall_rules'] == [] # Test: Load bad section type yml_file.write(""" application: - name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: image """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: Missing mandatory value in default keys yml_file.write(""" application: name: my_app package: # Missing image type: container_image fpga: image: image """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: Missing mandatory value in environment keys yml_file.write(""" application: name: my_app version: 1.0.0 package: # Missing image my_provider: type: container_image fpga: image: image """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: Value not in list yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image firewall_rules: - start_port: 1000 end_port: 1000 protocol: tcp # No a known direction direction: no_direction fpga: image: image """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: Bad value type yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: image # Count should be an int count: "1" """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: Bad value type in list yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: - image_slot0 - 1 count: 1 """) with pytest.raises(ConfigurationException): lint(yml_file) # Test: List of values yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: - image_slot0 - image_slot1 count: 1 """) lint(yml_file) # Test: Auto list conversion yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: image_slot0 count: 1 """) lint(yml_file) # Test: top level list bad value yml_file.write(""" application: name: my_app version: 1.0.0 package: type: container_image name: my_container_image fpga: image: 1 count: 1 """) with pytest.raises(ConfigurationException): lint(yml_file)
def test_host(tmpdir): """ Test host Args: tmpdir (py.path.local) tmpdir pytest fixture """ import accelpy._common as common import accelpy._host as accelpy_host from accelpy._host import Host, iter_hosts from accelpy.exceptions import (ConfigurationException, AccelizeException, AuthenticationException) from accelpy._application import Application from tests.test_core_terraform import mock_terraform_provider from tests.test_core_packer import mock_packer_provider from tests.test_core_ansible import mock_ansible_local from tests.test_core_application import mock_application source_dir = tmpdir.join('source').ensure(dir=True) # Mock ~/.accelize common_home_dir = common.HOME_DIR common.HOME_DIR = tmpdir.join('home').ensure(dir=True) # Mock config dir accelpy_host_config_dir = accelpy_host.CONFIG_DIR config_dir = tmpdir.join('config').ensure(dir=True) accelpy_host.CONFIG_DIR = str(config_dir) # Mock application definition file & provider specific configuration application = mock_application(source_dir) mock_terraform_provider(source_dir) artifact = mock_packer_provider(source_dir) source_dir.ensure('cred.json') # Tests try: # Test: Host generation with not arguments should raise with pytest.raises(ConfigurationException): Host() # Test: Create host with specified name + use as context manager name = 'testing' with Host(application=application, name=name, user_config=source_dir) as host: assert host.name == name assert name in str(host) assert name in repr(host) # Test: Create host with generated name host = Host(application=application, user_config=source_dir) assert host.name name = host.name # Test: Initialization host_config_dir = config_dir.join(name) assert host_config_dir.join('playbook.yml').isfile() assert host_config_dir.join('common.tf').isfile() assert host_config_dir.join('template.json').isfile() assert host_config_dir.join('application.yml').isfile() assert host._ansible assert host._application assert host._terraform assert host._packer # Test: Output values should raise as not applied with pytest.raises(ConfigurationException): assert host.private_ip # Test: Terraform plan assert host.plan() # Test: Output values should still raise if only planned with pytest.raises(ConfigurationException): assert host.private_ip # Test: Terraform apply host.apply(quiet=True) assert host_config_dir.join('terraform.tfstate').isfile() # Test: Output variable assert host.private_ip == "127.0.0.1" assert host.public_ip == "127.0.0.1" assert host.ssh_user == "user" assert host.ssh_private_key == str( host_config_dir.join('ssh_private.pem')) # Test: Terraform destroy host.destroy(quiet=True, delete=True) # Test: Do destroy on exit with Host(application=application, user_config=source_dir, destroy_on_exit=True, keep_config=False) as host: assert not config_dir.join( f'{host.name}/terraform.tfstate').isfile() host.apply(quiet=True) assert config_dir.join(f'{host.name}/terraform.tfstate').isfile() assert not config_dir.join(host.name).exists() # Test: Do not destroy on exit with Host(application=application, user_config=source_dir) as host: host_not_destroyed = host.name host.apply(quiet=True) assert config_dir.join(f'{host.name}/terraform.tfstate').isfile() assert config_dir.join(f'{host.name}/terraform.tfstate').isfile() # Test: Do not destroy on exit, but do not keep un-applied config with Host(application=application, user_config=source_dir, keep_config=False) as host: assert config_dir.join(host.name).exists() assert not config_dir.join(host.name).exists() # Test: Clean up in case of initialization error with pytest.raises(AccelizeException): Host(application='path_not_exists', user_config=source_dir, name='should_be_cleaned') assert not config_dir.join('should_be_cleaned').exists() # Test: Load existing host with Host(name=host_not_destroyed) as host: assert host.private_ip assert host._application # Test: Iter over host config_dir.join('latest').ensure() assert host_not_destroyed in tuple(host.name for host in iter_hosts()) # Test: Build image provider = 'testing' with Host(application=application, user_config=source_dir, provider=provider) as host: # Mock ansible playbook host_config_dir = config_dir.join(host.name) mock_ansible_local(host_config_dir) application_yaml = str(host_config_dir.join('application.yml')) # Test: Build image and with no application update assert Application( application_yaml)[provider]['package'][0]['name'] == 'my_image' host.build(quiet=True) assert Application( application_yaml)[provider]['package'][0]['name'] == 'my_image' # Test: Build image and update application host.build(quiet=True, update_application=True) assert Application( application_yaml)[provider]['package'][0]['name'] == artifact # Test: Missing Accelize DRM configuration application = mock_application( source_dir, override={'accelize_drm': { 'use_service': True }}) with pytest.raises(ConfigurationException): Host(application=application, user_config=source_dir, keep_config=False) # Test: Missing Accelize DRM credentials source_dir.join('cred.json').remove() application = mock_application(source_dir) with pytest.raises(AuthenticationException): Host(application=application, user_config=source_dir, keep_config=False) # Restore mocked config dir finally: accelpy_host.CONFIG_DIR = accelpy_host_config_dir common.HOME_DIR = common_home_dir
def test_lint(tmpdir): """ Test application definition file lint Args: tmpdir (py.path.local) tmpdir pytest fixture """ from accelpy._application import Application from accelpy.exceptions import ConfigurationException # Mock yaml definition file yml_file = tmpdir.join('application.yml') # Test: Load valid file yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image version: 1.0.0 # Override of value in provider my_provider: type: vm_image name: my_vm_image firewall_rules: - start_port: 1000 end_port: 1000 protocol: tcp direction: ingress - start_port: 1001 end_port: 1100 protocol: tcp direction: egress fpga: # Mandatory value only in provider my_provider: image: my_fpga_image """) app = Application(yml_file) # Test section _node assert isinstance(app['firewall_rules'], list) assert isinstance(app['application'], dict) # Test: get assert app['fpga']['image'] == [] assert app['my_provider']['fpga']['image'] == ['my_fpga_image'] assert app['package'][0]['type'] == 'container_image' assert app['my_provider']['package'][0]['type'] == 'vm_image' # Test: save and reload app['package'][0]['my_provider']['name'] = 'another_image' app.save() assert Application( yml_file)['my_provider']['package'][0]['name'] == 'another_image' # Test: provider assert app.providers == {'my_provider'} # Test: Load valid file with missing not mandatory section yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image """) app = Application(yml_file) assert app['firewall_rules'] == [] # Test: Load file with inferred list section from mapping yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: type: container_image name: my_container_image fpga: image: image """) app = Application(yml_file) assert app['package'][0]['type'] == 'container_image' # Test: Provider with reserved name yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image application: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Missing or empty package section yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: [] fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) yml_file.write(""" application: product_id: my_product_id version: 1.0.0 fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Load bad section type yml_file.write(""" application: - product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Missing mandatory value in default keys yml_file.write(""" application: product_id: my_product_id package: # Missing image - type: container_image fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Missing mandatory value in provider keys yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: # Missing image - my_provider: type: container_image fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Value not in list yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image firewall_rules: - start_port: 1000 end_port: 1000 protocol: tcp # No a known direction direction: no_direction fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Bad value type yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image # Count should be an int count: "1" """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Bad value type for str type yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image # Driver version should be a str driver_version: 1.0 """) Application(yml_file) # Test: Bad value type in list yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: - image_slot0 - 1 count: 1 """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: List of values yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: - image_slot0 - image_slot1 count: 1 """) Application(yml_file) # Test: Auto list conversion yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image_slot0 count: 1 """) Application(yml_file) # Test: top level list bad value yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: 1 count: 1 """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Extra section should not raise yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image extra_section: extra_key: value """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Extra key should raise yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image extra_key: value """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Extra key should raise in provider yml_file.write(""" application: product_id: my_product_id version: 1.0.0 package: - type: container_image name: my_container_image fpga: image: image my_provider: extra_key: value """) with pytest.raises(ConfigurationException): Application(yml_file) # Test: Value does not match regex yml_file.write(""" application: product_id: my_product_id version: 1.0.0.0.0 package: - type: container_image name: my_container_image fpga: image: image """) with pytest.raises(ConfigurationException): Application(yml_file)