From 1837a64de5d0c1ddc32edb316a05860224011637 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Fri, 1 Dec 2023 17:39:35 +0100 Subject: [PATCH] add test fixture --- lso/playbook.py | 5 ++++- lso/routes/ip_trunk.py | 22 ++++++++++++++++------ lso/routes/playbook.py | 6 ++++-- lso/routes/router.py | 6 ++++-- test/conftest.py | 10 ++++++++++ 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lso/playbook.py b/lso/playbook.py index 4b6dcfe..25dc8ee 100644 --- a/lso/playbook.py +++ b/lso/playbook.py @@ -12,7 +12,7 @@ import ansible_runner import requests import xmltodict from dictdiffer import diff -from fastapi import status +from fastapi import Response, status from pydantic import BaseModel, HttpUrl from lso import config @@ -181,6 +181,7 @@ def run_playbook( extra_vars: dict[str, Any], inventory: dict[str, Any] | str, callback: HttpUrl, + response: Response, ) -> PlaybookLaunchResponse: """Run an Ansible playbook against a specified inventory. @@ -193,10 +194,12 @@ def run_playbook( :rtype: :class:`PlaybookLaunchResponse` """ if not Path.exists(playbook_path): + response.status_code = status.HTTP_404_NOT_FOUND msg = f"Filename '{playbook_path}' does not exist." return playbook_launch_error(msg) if not ansible_runner.utils.isinventory(inventory): + response.status_code = status.HTTP_400_BAD_REQUEST msg = "Invalid inventory provided. Should be a string, or JSON object." return playbook_launch_error(msg) diff --git a/lso/routes/ip_trunk.py b/lso/routes/ip_trunk.py index 086ec21..3ec7e59 100644 --- a/lso/routes/ip_trunk.py +++ b/lso/routes/ip_trunk.py @@ -1,6 +1,6 @@ """Routes for handling events related to the IP trunk service.""" -from fastapi import APIRouter +from fastapi import APIRouter, Response from pydantic import BaseModel, HttpUrl from lso.playbook import PlaybookLaunchResponse, get_playbook_path, run_playbook @@ -72,7 +72,7 @@ class IPTrunkDeleteParams(IPTrunkParams): @router.post("/") -def provision_ip_trunk(params: IPTrunkProvisioningParams) -> PlaybookLaunchResponse: +def provision_ip_trunk(params: IPTrunkProvisioningParams, response: Response) -> PlaybookLaunchResponse: """Launch a playbook to provision a new IP trunk service. The response will contain either a job ID, or error information. @@ -80,6 +80,7 @@ def provision_ip_trunk(params: IPTrunkProvisioningParams) -> PlaybookLaunchRespo :param params: The parameters that define the new subscription object that is to be deployed. :type params: :class:`IPTrunkProvisioningParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -99,15 +100,17 @@ def provision_ip_trunk(params: IPTrunkProvisioningParams) -> PlaybookLaunchRespo f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n", extra_vars=extra_vars, callback=params.callback, + response=response, ) @router.put("/") -def modify_ip_trunk(params: IPTrunkModifyParams) -> PlaybookLaunchResponse: +def modify_ip_trunk(params: IPTrunkModifyParams, response: Response) -> PlaybookLaunchResponse: """Launch a playbook that modifies an existing IP trunk service. :param params: The parameters that define the change in configuration. :type params: :class:`IPTrunkModifyParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -127,16 +130,18 @@ def modify_ip_trunk(params: IPTrunkModifyParams) -> PlaybookLaunchResponse: f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n", extra_vars=extra_vars, callback=params.callback, + response=response, ) @router.delete("/") -def delete_ip_trunk(params: IPTrunkDeleteParams) -> PlaybookLaunchResponse: +def delete_ip_trunk(params: IPTrunkDeleteParams, response: Response) -> PlaybookLaunchResponse: """Launch a playbook that deletes an existing IP trunk service. :param params: Parameters that define the subscription that should get terminated. :type params: :class:`IPTrunkDeleteParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -156,16 +161,18 @@ def delete_ip_trunk(params: IPTrunkDeleteParams) -> PlaybookLaunchResponse: f"{params.subscription['iptrunk']['iptrunk_sides'][1]['iptrunk_side_node']['router_fqdn']}\n", extra_vars=extra_vars, callback=params.callback, + response=response, ) @router.post("/perform_check") -def check_ip_trunk(params: IPTrunkCheckParams) -> PlaybookLaunchResponse: +def check_ip_trunk(params: IPTrunkCheckParams, response: Response) -> PlaybookLaunchResponse: """Launch a playbook that performs a check on an IP trunk service instance. :param params: Parameters that define the check that is going to be executed, including on which relevant subscription. :type params: :class:`IPTrunkCheckParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -178,17 +185,19 @@ def check_ip_trunk(params: IPTrunkCheckParams) -> PlaybookLaunchResponse: inventory=params.subscription["iptrunk"]["iptrunk_sides"][0]["iptrunk_side_node"]["router_fqdn"], extra_vars=extra_vars, callback=params.callback, + response=response, ) @router.post("/migrate") -def migrate_ip_trunk(params: IPTrunkMigrationParams) -> PlaybookLaunchResponse: +def migrate_ip_trunk(params: IPTrunkMigrationParams, response: Response) -> PlaybookLaunchResponse: """Launch a playbook to provision a new IP trunk service. The response will contain either a job ID, or error information. :param params: The parameters that define the new subscription object that is to be migrated. :type params: :class:`IPTrunkMigrationParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -212,4 +221,5 @@ def migrate_ip_trunk(params: IPTrunkMigrationParams) -> PlaybookLaunchResponse: f"{params.new_side['new_node']['router']['router_fqdn']}\n", extra_vars=extra_vars, callback=params.callback, + response=response, ) diff --git a/lso/routes/playbook.py b/lso/routes/playbook.py index 670848e..8827ea5 100644 --- a/lso/routes/playbook.py +++ b/lso/routes/playbook.py @@ -2,7 +2,7 @@ from typing import Any -from fastapi import APIRouter +from fastapi import APIRouter, Response from pydantic import BaseModel, HttpUrl from lso.playbook import PlaybookLaunchResponse, get_playbook_path, run_playbook @@ -29,12 +29,13 @@ class PlaybookRunParams(BaseModel): @router.post("/") -def run_playbook_endpoint(params: PlaybookRunParams) -> PlaybookLaunchResponse: +def run_playbook_endpoint(params: PlaybookRunParams, response: Response) -> PlaybookLaunchResponse: """Launch an Ansible playbook to modify or deploy a subscription instance. The response will contain either a job ID, or error information. :param params :class:`PlaybookRunParams`: Parameters for executing a playbook. + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -43,4 +44,5 @@ def run_playbook_endpoint(params: PlaybookRunParams) -> PlaybookLaunchResponse: extra_vars=params.extra_vars, inventory=params.inventory, callback=params.callback, + response=response, ) diff --git a/lso/routes/router.py b/lso/routes/router.py index 6753f63..6861e5b 100644 --- a/lso/routes/router.py +++ b/lso/routes/router.py @@ -1,6 +1,6 @@ """Routes for handling device/base_config-related requests.""" -from fastapi import APIRouter +from fastapi import APIRouter, Response from pydantic import BaseModel, HttpUrl from lso import playbook @@ -35,11 +35,12 @@ class NodeProvisioningParams(BaseModel): @router.post("/") -async def provision_node(params: NodeProvisioningParams) -> playbook.PlaybookLaunchResponse: +async def provision_node(params: NodeProvisioningParams, response: Response) -> playbook.PlaybookLaunchResponse: """Launch a playbook to provision a new node. The response will contain either a job id or error information. :param params: Parameters for provisioning a new node :type params: :class:`NodeProvisioningParams` + :param Response response: A FastAPI response used for returning error codes if needed :return: Response from the Ansible runner, including a run ID. :rtype: :class:`lso.playbook.PlaybookLaunchResponse` """ @@ -55,4 +56,5 @@ async def provision_node(params: NodeProvisioningParams) -> playbook.PlaybookLau inventory=f"{params.subscription['router']['router_fqdn']}", extra_vars=extra_vars, callback=params.callback, + response=response, ) diff --git a/test/conftest.py b/test/conftest.py index cb4725e..13407eb 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -4,6 +4,7 @@ import tempfile from collections.abc import Callable, Generator from io import StringIO from typing import Any +from unittest.mock import patch import pytest from faker import Faker @@ -55,3 +56,12 @@ def client(data_config_filename: str) -> TestClient: @pytest.fixture(scope="session") def faker() -> Faker: return Faker(locale="en_GB") + + +@pytest.fixture(scope="session", autouse=True) +def path_exists() -> bool: + """A patch that prevents all test requests from failing since the playbook file does not exist on the filesystem.""" + with patch("pathlib.Path.exists") as mock_patch: + mock_patch.return_value = True + + yield mock_patch -- GitLab