From 7952f16032a9dc4783261bc4baee693cad9c0bc9 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Mon, 24 May 2021 13:24:22 -0400 Subject: [PATCH] basic list proxy functionality (#905) --- src/api-service/__app__/proxy/__init__.py | 51 ++++++++++++++--------- src/cli/onefuzz/api.py | 3 ++ src/pytypes/onefuzztypes/requests.py | 21 +++++++--- src/pytypes/onefuzztypes/responses.py | 13 +++++- 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/api-service/__app__/proxy/__init__.py b/src/api-service/__app__/proxy/__init__.py index 5734d66f33..ad333a4eae 100644 --- a/src/api-service/__app__/proxy/__init__.py +++ b/src/api-service/__app__/proxy/__init__.py @@ -9,7 +9,7 @@ from onefuzztypes.enums import ErrorCode, VmState from onefuzztypes.models import Error from onefuzztypes.requests import ProxyCreate, ProxyDelete, ProxyGet, ProxyReset -from onefuzztypes.responses import BoolResult, ProxyGetResult +from onefuzztypes.responses import BoolResult, ProxyGetResult, ProxyInfo, ProxyList from ..onefuzzlib.endpoint_authorization import call_if_user from ..onefuzzlib.events import get_events @@ -36,26 +36,37 @@ def get(req: func.HttpRequest) -> func.HttpResponse: if isinstance(request, Error): return not_ok(request, context="ProxyGet") - scaleset = Scaleset.get_by_id(request.scaleset_id) - if isinstance(scaleset, Error): - return not_ok(scaleset, context="ProxyGet") - - proxy = Proxy.get_or_create(scaleset.region) - forwards = ProxyForward.search_forward( - scaleset_id=request.scaleset_id, - machine_id=request.machine_id, - dst_port=request.dst_port, - ) - if not forwards: - return not_ok( - Error( - code=ErrorCode.INVALID_REQUEST, - errors=["no forwards for scaleset and node"], - ), - context="debug_proxy get", + if ( + request.scaleset_id is not None + and request.machine_id is not None + and request.dst_port is not None + ): + scaleset = Scaleset.get_by_id(request.scaleset_id) + if isinstance(scaleset, Error): + return not_ok(scaleset, context="ProxyGet") + + proxy = Proxy.get_or_create(scaleset.region) + forwards = ProxyForward.search_forward( + scaleset_id=request.scaleset_id, + machine_id=request.machine_id, + dst_port=request.dst_port, ) - - return ok(get_result(forwards[0], proxy)) + if not forwards: + return not_ok( + Error( + code=ErrorCode.INVALID_REQUEST, + errors=["no forwards for scaleset and node"], + ), + context="debug_proxy get", + ) + + return ok(get_result(forwards[0], proxy)) + else: + proxies = [ + ProxyInfo(region=x.region, proxy_id=x.proxy_id, state=x.state) + for x in Proxy.search() + ] + return ok(ProxyList(proxies=proxies)) def post(req: func.HttpRequest) -> func.HttpResponse: diff --git a/src/cli/onefuzz/api.py b/src/cli/onefuzz/api.py index 4eaa1a4d0f..23c2f4a958 100644 --- a/src/cli/onefuzz/api.py +++ b/src/cli/onefuzz/api.py @@ -1468,6 +1468,9 @@ def create( ), ) + def list(self) -> responses.ProxyList: + return self._req_model("GET", responses.ProxyList, data=requests.ProxyGet()) + class Command: def __init__(self, onefuzz: "Onefuzz", logger: logging.Logger): diff --git a/src/pytypes/onefuzztypes/requests.py b/src/pytypes/onefuzztypes/requests.py index ede719479c..df7c32ebb2 100644 --- a/src/pytypes/onefuzztypes/requests.py +++ b/src/pytypes/onefuzztypes/requests.py @@ -3,10 +3,10 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from uuid import UUID -from pydantic import AnyHttpUrl, BaseModel, Field, validator +from pydantic import AnyHttpUrl, BaseModel, Field, root_validator, validator from .consts import ONE_HOUR, SEVEN_DAYS from .enums import ( @@ -107,9 +107,20 @@ class PoolStop(BaseRequest): class ProxyGet(BaseRequest): - scaleset_id: UUID - machine_id: UUID - dst_port: int + scaleset_id: Optional[UUID] + machine_id: Optional[UUID] + dst_port: Optional[int] + + @root_validator() + def check_proxy_get(cls, value: Any) -> Any: + check_keys = ["scaleset_id", "machine_id", "dst_port"] + included = [x in value for x in check_keys] + if any(included) and not all(included): + raise ValueError( + "ProxyGet must provide all or none of the following: %s" + % ", ".join(check_keys) + ) + return value class ProxyCreate(BaseRequest): diff --git a/src/pytypes/onefuzztypes/responses.py b/src/pytypes/onefuzztypes/responses.py index 9fd4e95cf5..24f62151b1 100644 --- a/src/pytypes/onefuzztypes/responses.py +++ b/src/pytypes/onefuzztypes/responses.py @@ -3,11 +3,12 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -from typing import Dict, Optional +from typing import Dict, List, Optional from uuid import UUID from pydantic import BaseModel +from .enums import VmState from .models import Forward, NodeCommandEnvelope from .primitives import Region @@ -25,6 +26,16 @@ class ProxyGetResult(BaseResponse): forward: Forward +class ProxyInfo(BaseModel): + region: Region + proxy_id: UUID + state: VmState + + +class ProxyList(BaseResponse): + proxies: List[ProxyInfo] + + class Version(BaseResponse): git: str build: str