From 5749c3a13450abe6ab897c907b9e3295ea45a4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20M=C3=A9ry?= Date: Mon, 13 Mar 2023 14:48:45 +0100 Subject: [PATCH] chore: remove legacy gateway integration --- scw_serverless/cli.py | 39 +------ scw_serverless/deploy/gateway/__init__.py | 2 - scw_serverless/deploy/gateway/client.py | 44 ------- scw_serverless/deploy/gateway/manager.py | 108 ------------------ scw_serverless/deploy/gateway/models.py | 39 ------- scw_serverless/utils/config.py | 30 ----- tests/dev/gateway.py | 14 --- tests/test_deploy/test_gateway/__init__.py | 0 .../test_deploy/test_gateway/test_manager.py | 93 --------------- 9 files changed, 1 insertion(+), 368 deletions(-) delete mode 100644 scw_serverless/deploy/gateway/__init__.py delete mode 100644 scw_serverless/deploy/gateway/client.py delete mode 100644 scw_serverless/deploy/gateway/manager.py delete mode 100644 scw_serverless/deploy/gateway/models.py delete mode 100644 scw_serverless/utils/config.py delete mode 100644 tests/dev/gateway.py delete mode 100644 tests/test_deploy/test_gateway/__init__.py delete mode 100644 tests/test_deploy/test_gateway/test_manager.py diff --git a/scw_serverless/cli.py b/scw_serverless/cli.py index 531209b..9a01f9d 100644 --- a/scw_serverless/cli.py +++ b/scw_serverless/cli.py @@ -9,9 +9,8 @@ ) from scw_serverless.config.generators.terraform import TerraformGenerator from scw_serverless.dependencies_manager import DependenciesManager -from scw_serverless.deploy import backends, gateway +from scw_serverless.deploy import backends from scw_serverless.logger import DEFAULT, get_logger -from scw_serverless.utils.config import Config from scw_serverless.utils.credentials import DEFAULT_REGION, get_scw_client from scw_serverless.utils.loader import get_app_instance @@ -84,18 +83,6 @@ def cli() -> None: show_default=True, help="Select the backend used to deploy", ) -@click.option( - "--gw-id", - "-g", - "gateway_id", - envvar="SCW_APIGW_ID", - help="API Gateway uuid to use with function endpoints", -) -@click.option( - "--api-gw-host", - envvar="SCW_API_GW_HOST", - help="Host of the API to manage gateways", -) def deploy( file: Path, backend: Literal["api", "serverless"], @@ -104,8 +91,6 @@ def deploy( secret_key: Optional[str] = None, project_id: Optional[str] = None, region: Optional[str] = None, - gateway_id: Optional[str] = None, - api_gw_host: Optional[str] = None, ) -> None: """Deploy your functions to Scaleway. @@ -138,28 +123,6 @@ def deploy( deploy_backend.deploy() - needs_gateway = any(function.gateway_route for function in app_instance.functions) - if not needs_gateway: - return - config = Config(api_gw_host, gateway_id).update_from_config_file() - # If gateway_id is not configured, gateway_domains needs to be set - is_gateway_configured = config.gateway_id or app_instance.gateway_domains - if not is_gateway_configured: - raise RuntimeError( - "Deploying a routed functions requires a" - + "gateway_id or a gateway_domain to be configured" - ) - if not config.api_gw_host: - raise RuntimeError("No api gateway host was configured") - # Update the gateway - manager = gateway.GatewayManager( - app_instance, - client, - config.gateway_id, - gateway.GatewayClient(config.api_gw_host), - ) - manager.update_gateway_routes() - @cli.command() @CLICK_ARG_FILE diff --git a/scw_serverless/deploy/gateway/__init__.py b/scw_serverless/deploy/gateway/__init__.py deleted file mode 100644 index bbea36f..0000000 --- a/scw_serverless/deploy/gateway/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .client import GatewayClient -from .manager import GatewayManager diff --git a/scw_serverless/deploy/gateway/client.py b/scw_serverless/deploy/gateway/client.py deleted file mode 100644 index fa6f5f9..0000000 --- a/scw_serverless/deploy/gateway/client.py +++ /dev/null @@ -1,44 +0,0 @@ -import dataclasses - -import requests - -from scw_serverless.deploy.gateway.models import GatewayInput, GatewayOutput - - -class GatewayClient: - """Client for the API to manage gateways.""" - - def __init__(self, base_url: str, *args, **kwargs): - self.base_url: str = base_url - self.gateways_base_url: str = f"{self.base_url}/gateways" - self.session: requests.Session = requests.Session(*args, **kwargs) - self.session.headers.update( - {"Content-type": "application/json", "Accept": "text/plain"} - ) - - def create_gateway(self, gateway: GatewayInput) -> GatewayOutput: - """Creates a gateway.""" - res = self.session.post( - self.gateways_base_url, json=dataclasses.asdict(gateway) - ) - return GatewayOutput.from_dict(res.json()) - - def get_gateway(self, uuid: str) -> GatewayOutput: - """Gets details on a gateway.""" - res = self.session.get(f"{self.gateways_base_url}/{uuid}") - return GatewayOutput.from_dict(res.json()) - - def update_gateway(self, uuid: str, gateway: GatewayInput) -> GatewayOutput: - """Updates a gateway.""" - res = self.session.put( - f"{self.gateways_base_url}/{uuid}", json=dataclasses.asdict(gateway) - ) - return GatewayOutput.from_dict(res.json()) - - def delete_gateway(self, uuid: str) -> None: - """Deletes a gateway. - - Note: raises if status is not OK - """ - res = self.session.delete(f"{self.gateways_base_url}/{uuid}") - res.raise_for_status() diff --git a/scw_serverless/deploy/gateway/manager.py b/scw_serverless/deploy/gateway/manager.py deleted file mode 100644 index 04fcad1..0000000 --- a/scw_serverless/deploy/gateway/manager.py +++ /dev/null @@ -1,108 +0,0 @@ -from typing import Optional - -import scaleway.function.v1beta1 as sdk -from scaleway import Client - -from scw_serverless.app import Serverless -from scw_serverless.deploy.gateway.client import GatewayClient -from scw_serverless.deploy.gateway.models import GatewayInput, Route -from scw_serverless.logger import get_logger - - -class GatewayManager: - """Manages API Gateways.""" - - def __init__( - self, - app_instance: Serverless, - sdk_client: Client, - gateway_uuid: Optional[str], - gateway_client: GatewayClient, - ): - self.api = sdk.FunctionV1Beta1API(sdk_client) - self.app_instance = app_instance - self.gateway_client = gateway_client - self.gateway_uuid = gateway_uuid - self.logger = get_logger() - - def _get_routes(self, deployed_fns: dict[str, sdk.Function]) -> list[Route]: - routes = [] - # Compare with the configured functions - for function in self.app_instance.functions: - if not function.gateway_route: - continue - if function.name not in deployed_fns: - raise RuntimeError( - f"Could not find function {function.name} in namespace" - ) - deployed = deployed_fns[function.name] - routes.append( - Route( - path=function.gateway_route.path, - target=deployed.domain_name, - methods=[ - str(method) for method in function.gateway_route.methods or [] - ], - ) - ) - return routes - - def _list_deployed_fns(self) -> dict[str, sdk.Function]: - namespace_name = self.app_instance.service_name - namespaces = self.api.list_namespaces_all(name=namespace_name) - if not namespaces: - raise ValueError(f"Could not find a namespace with name: {namespace_name}") - namespace_id = namespaces[0].id - # Get the list of the deployed functions - return { - function.name: function - for function in self.api.list_functions_all(namespace_id=namespace_id) - } - - def _deploy_to_existing(self, routes: list[Route]) -> list[str]: - assert self.gateway_uuid - self.logger.default(f"Updating gateway {self.gateway_uuid} configuration...") - gateway = self.gateway_client.get_gateway(self.gateway_uuid) - domains = sorted(set(self.app_instance.gateway_domains + gateway.domains)) - self.gateway_client.update_gateway( - self.gateway_uuid, GatewayInput(domains, routes) - ) - return domains - - def _deploy_to_new(self, routes: list[Route]) -> list[str]: - self.logger.default("No gateway was configured, creating a new gateway...") - gateway = self.gateway_client.create_gateway( - GatewayInput(self.app_instance.gateway_domains, routes) - ) - self.logger.success(f"Successfully created gateway {gateway.uuid}") - return self.app_instance.gateway_domains - - def _display_routes(self, domains: list[str]): - prefix = domains[0] if domains else "" - routed = filter( - lambda function: function.gateway_route, self.app_instance.functions - ) - self.logger.default("The following functions were configured: ") - for function in routed: - assert function.gateway_route - methods = ",".join( - [str(method) for method in function.gateway_route.methods or []] - ) - row = f"\t{function.name} on {prefix + function.gateway_route.path}" - if methods: - row += " with " + methods - self.logger.default(row) - - def update_gateway_routes(self) -> None: - """Updates the configurations of the API gateway.""" - - deployed_fns = self._list_deployed_fns() - routes = self._get_routes(deployed_fns) - - domains = None - if self.gateway_uuid: - domains = self._deploy_to_existing(routes) - else: - domains = self._deploy_to_new(routes) - - self._display_routes(domains) diff --git a/scw_serverless/deploy/gateway/models.py b/scw_serverless/deploy/gateway/models.py deleted file mode 100644 index 7b8156b..0000000 --- a/scw_serverless/deploy/gateway/models.py +++ /dev/null @@ -1,39 +0,0 @@ -from dataclasses import dataclass -from typing import Any - - -@dataclass -class Route: - """Represents a Gateway Route from path to target with methods.""" - - path: str - target: str - methods: list[str] - - @staticmethod - def from_dict(data: dict[str, Any]): - """Converts from a dict.""" - return Route(path=data["path"], target=data["target"], methods=data["methods"]) - - -@dataclass -class GatewayOutput: - """Represents a Gateway returned from the API.""" - - uuid: str - domains: list[str] - routes: list[Route] - - @staticmethod - def from_dict(data: dict[str, Any]): - """Converts from a dict.""" - routes = [Route.from_dict(route) for route in data["routes"]] - return GatewayOutput(uuid=data["uuid"], domains=data["domains"], routes=routes) - - -@dataclass -class GatewayInput: - """Represents a Gateway creation request to the API.""" - - domains: list[str] - routes: list[Route] diff --git a/scw_serverless/utils/config.py b/scw_serverless/utils/config.py deleted file mode 100644 index 899533c..0000000 --- a/scw_serverless/utils/config.py +++ /dev/null @@ -1,30 +0,0 @@ -from dataclasses import asdict, dataclass -from pathlib import Path -from typing import Optional - -import yaml - -DEFAULT_CONFIG_PATH = "~/.config/scw/py_api.yaml" - - -@dataclass -class Config: - """Utility class to hold configuration parameters.""" - - api_gw_host: Optional[str] = None # Host for the API Gateway controller api - gateway_id: Optional[str] = None # Default API Gateway uuid - - def update_from_config_file(self, config_path: Optional[str] = None) -> "Config": - """Update an existing config instance whith values passed via a config file.""" - config_path = config_path if config_path else DEFAULT_CONFIG_PATH - config_path = Path(DEFAULT_CONFIG_PATH).expanduser() - - if not config_path.exists(): - return self - - with open(config_path, mode="rt", encoding="utf-8") as file: - config = yaml.safe_load(file) - config |= asdict(self) - return Config(**config) - - return self diff --git a/tests/dev/gateway.py b/tests/dev/gateway.py deleted file mode 100644 index 394d0b5..0000000 --- a/tests/dev/gateway.py +++ /dev/null @@ -1,14 +0,0 @@ -from scw_serverless.app import Serverless - -app = Serverless("integration-tests-gateway", gateway_domains=["example.org"]) - - -@app.get("/", privacy="public") -def hello_world(_event: dict, _context: dict): - """handle a request to the function - Args: - event (dict): request params - context (dict): function call metadata - """ - - return "Hello World from gateway!" diff --git a/tests/test_deploy/test_gateway/__init__.py b/tests/test_deploy/test_gateway/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_deploy/test_gateway/test_manager.py b/tests/test_deploy/test_gateway/test_manager.py deleted file mode 100644 index 5b0da8c..0000000 --- a/tests/test_deploy/test_gateway/test_manager.py +++ /dev/null @@ -1,93 +0,0 @@ -from unittest.mock import MagicMock - -import pytest - -from scw_serverless.config.route import HTTPMethod -from scw_serverless.deploy import gateway -from scw_serverless.deploy.gateway.models import GatewayInput, GatewayOutput, Route -from tests.dev.gateway import app - -MOCK_UUID = "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" -HELLO_WORLD_MOCK_ENDPOINT = ( - "https://helloworldfunctionnawns8i8vo-hello-world.functions.fnc.fr-par.scw.cloud" -) -PROJECT_ID = "projecti-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" - - -@pytest.fixture(name="app_gateway_manager") -def _app_gateway_manager() -> gateway.GatewayManager: - mock_client, gateway_client = MagicMock(), MagicMock() - gateway_uuid = MOCK_UUID - manager = gateway.GatewayManager(app, mock_client, gateway_uuid, gateway_client) - manager.api = mock_client - return manager - - -def test_gateway_manager_get_routes(app_gateway_manager: gateway.GatewayManager): - function = MagicMock() - function.domain_name = HELLO_WORLD_MOCK_ENDPOINT - routes = app_gateway_manager._get_routes({"hello-world": function}) - - assert len(routes) == 1 - assert ( - Route(path="/", target=HELLO_WORLD_MOCK_ENDPOINT, methods=[str(HTTPMethod.GET)]) - in routes - ) - - -def test_gateway_manager_update_gateway_routes_with_gw_id( - app_gateway_manager: gateway.GatewayManager, -): - api: MagicMock = app_gateway_manager.api # type: ignore - client: MagicMock = app_gateway_manager.gateway_client # type: ignore - - function = MagicMock() - function.name = "hello-world" - function.domain_name = HELLO_WORLD_MOCK_ENDPOINT - api.list_functions_all.return_value = [function] - client.get_gateway.return_value = GatewayOutput(MOCK_UUID, ["toto.fr"], []) - - app_gateway_manager.update_gateway_routes() - - client.get_gateway.assert_called_once_with(MOCK_UUID) - client.update_gateway.assert_called_once_with( - MOCK_UUID, - GatewayInput( - ["example.org", "toto.fr"], - [ - Route( - path="/", - target=HELLO_WORLD_MOCK_ENDPOINT, - methods=[str(HTTPMethod.GET)], - ) - ], - ), - ) - - -def test_gateway_manager_update_gateway_routes_without_gw_id( - app_gateway_manager: gateway.GatewayManager, -): - app_gateway_manager.gateway_uuid = None # Set the gateway_uuid to None - - api: MagicMock = app_gateway_manager.api # type: ignore - client: MagicMock = app_gateway_manager.gateway_client # type: ignore - - function = MagicMock() - function.name = "hello-world" - function.domain_name = HELLO_WORLD_MOCK_ENDPOINT - api.list_functions_all.return_value = [function] - app_gateway_manager.update_gateway_routes() - - client.create_gateway.assert_called_once_with( - GatewayInput( - ["example.org"], - [ - Route( - path="/", - target=HELLO_WORLD_MOCK_ENDPOINT, - methods=[str(HTTPMethod.GET)], - ) - ], - ), - )