From 628fb775e841e7e91eebd5a81c54abf323796cbf Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Tue, 11 Apr 2023 12:27:36 +0200 Subject: [PATCH] State loading from toml --- examples/echoclient.toml | 13 +++ examples/echoserver.toml | 34 ++++++++ phasmplatform/common/container.py | 14 +-- phasmplatform/common/image.py | 30 ++++++- phasmplatform/common/state.py | 103 +++++++++++++++++++++++ phasmplatform/worker/__main__.py | 14 +-- phasmplatform/worker/runners/base.py | 3 - phasmplatform/worker/runners/wasmtime.py | 8 -- 8 files changed, 193 insertions(+), 26 deletions(-) create mode 100644 examples/echoclient.toml create mode 100644 examples/echoserver.toml create mode 100644 phasmplatform/common/state.py diff --git a/examples/echoclient.toml b/examples/echoclient.toml new file mode 100644 index 0000000..049c142 --- /dev/null +++ b/examples/echoclient.toml @@ -0,0 +1,13 @@ +[echoclient-image] +apiVersion = "v0" +kind = "Image" + +path = "examples/echoclient.wasm" +hash = "sha256@84cb22d12dfdd6b05cb906f6db83d59f473c9df85a33822f696344af2b92b502" + +[echoclient-container] +apiVersion = "v0" +kind = "Container" + +image = "echoclient-image" +runtime = "wasmtime" diff --git a/examples/echoserver.toml b/examples/echoserver.toml new file mode 100644 index 0000000..f48dc75 --- /dev/null +++ b/examples/echoserver.toml @@ -0,0 +1,34 @@ +[echoserver-image] +apiVersion = "v0" +kind = "Image" + +path = "examples/echoserver.wasm" +hash = "sha256@dfe03b4f7ce5e921931f8715384e35a6776fdc28837e42ffa04305bbadffcfc9" + +[echoserver-container-0] +apiVersion = "v0" +kind = "Container" + +image = "echoserver-image" +runtime = "wasmtime" + +[echoserver-container-1] +apiVersion = "v0" +kind = "Container" + +image = "echoserver-image" +runtime = "wasmtime" + + +[echoserver-service] +apiVersion = "v0" +kind = "Service" +name = "echoserver" + +[echoserver-service.container] +byName = "echoserver-container-*" + +[[echoserver-service.methods]] +name = "echo" +returns = "none" +args = [ {name = "msg", type = "bytes"} ] diff --git a/phasmplatform/common/container.py b/phasmplatform/common/container.py index 07985dc..f09e0af 100644 --- a/phasmplatform/common/container.py +++ b/phasmplatform/common/container.py @@ -1,15 +1,15 @@ -from typing import List - from .image import Image -from .method import Method class Container: - __slots__ = ('image', 'methods', ) + __slots__ = ('image', 'runtime', ) image: Image - methods: List[Method] + runtime: str - def __init__(self, image: Image, methods: List[Method]) -> None: + def __init__(self, image: Image, runtime: str) -> None: self.image = image - self.methods = methods + self.runtime = runtime + + def __repr__(self) -> str: + return f'Container({repr(self.image)}, {repr(self.runtime)})' diff --git a/phasmplatform/common/image.py b/phasmplatform/common/image.py index 2452d1a..be246e0 100644 --- a/phasmplatform/common/image.py +++ b/phasmplatform/common/image.py @@ -1,7 +1,31 @@ -class Image: - __slots__ = ('hash', ) +from typing import Optional + +class Image: + __slots__ = ('path', 'hash', ) + + path: Optional[str] hash: str - def __init__(self, hash: str) -> None: + def __init__(self, path: Optional[str], hash: str) -> None: + self.path = path self.hash = hash + + def __repr__(self) -> str: + return f'Image({repr(self.path)}, {repr(self.hash)})' + + +class ImageReference(Image): + __slots__ = ('name', ) + + name: str + + def __init__(self, name: str) -> None: + # Intentionally do not call super() + # This will cause AttributeError exceptions when someone + # tries to access an image that's only a reference + + self.name = name + + def __repr__(self) -> str: + return f'ImageReference({repr(self.name)})' diff --git a/phasmplatform/common/state.py b/phasmplatform/common/state.py new file mode 100644 index 0000000..6e52fab --- /dev/null +++ b/phasmplatform/common/state.py @@ -0,0 +1,103 @@ +from typing import TYPE_CHECKING, Any, BinaryIO, Dict, List + +from .container import Container +from .image import Image, ImageReference +from .method import Method +from .service import Service + + +if TYPE_CHECKING: + import tomli as tomllib +else: + try: + import tomllib + except ImportError: + import tomli as tomllib + + +class State: + __slots__ = ('images', 'containers', 'services', ) + + images: List[Image] + containers: List[Container] + services: List[Service] + + def __init__( + self, + images: List[Image], + containers: List[Container], + services: List[Service], + ) -> None: + self.images = images + self.containers = containers + self.services = services + + def __repr__(self) -> str: + return f'State({repr(self.images)}, {repr(self.containers)}, {repr(self.services)})' + + +def image_from_toml(spec: Dict[str, Any]) -> Image: + """ + Loads an Image spec from toml + """ + return Image(str(spec['path']), str(spec['hash'])) + + +def container_from_toml(spec: Dict[str, Any]) -> Container: + """ + Loads a Container spec from toml + """ + return Container(ImageReference(str(spec['image'])), str(spec['runtime'])) + + +def service_from_toml(spec: Dict[str, Any]) -> Service: + """ + Loads a Service spec from toml + """ + methods: List[Method] = [] + + return Service(str(spec['name']), methods) + + +def from_toml(toml: BinaryIO) -> State: + """ + Loads the given toml and builds a state instance out of it + """ + toml_dict = tomllib.load(toml) + + images: List[Image] = [] + images_by_name: Dict[str, Image] = {} + containers: List[Container] = [] + services: List[Service] = [] + + for name, spec in toml_dict.items(): + if spec['apiVersion'] != 'v0': + raise NotImplementedError('apiVersion', spec['apiVersion']) + + if spec['kind'] == 'Image': + image = image_from_toml(spec) + images.append(image) + + assert name not in images_by_name, f'Duplicate image name: {name}' + images_by_name[name] = image + continue + + if spec['kind'] == 'Container': + containers.append(container_from_toml(spec)) + continue + + if spec['kind'] == 'Service': + services.append(service_from_toml(spec)) + continue + + raise NotImplementedError(spec) + + for container in containers: + if not isinstance(container.image, ImageReference): + continue + + image_opt = images_by_name.get(container.image.name, None) + assert image_opt is not None, f'Image reference not resolved: {container.image.name}' + container.image = image_opt + + return State(images, containers, services) diff --git a/phasmplatform/worker/__main__.py b/phasmplatform/worker/__main__.py index de93cd4..4d8237a 100644 --- a/phasmplatform/worker/__main__.py +++ b/phasmplatform/worker/__main__.py @@ -6,11 +6,12 @@ import time from queue import Empty, Queue from phasmplatform.common import valuetype -from phasmplatform.common.config import from_toml +from phasmplatform.common.config import from_toml as config_from_toml from phasmplatform.common.method import Method, MethodArgument, MethodCall from phasmplatform.common.router import MethodCallRouterInterface from phasmplatform.common.service import Service, ServiceDiscoveryInterface from phasmplatform.common.value import NoneValue +from phasmplatform.common.state import from_toml as state_from_toml from .runners.base import RunnerInterface from .runners.wasmtime import WasmTimeRunner @@ -23,7 +24,6 @@ def runner_thread(runner: RunnerInterface, queue: Queue[MethodCall]) -> None: except Empty: break - print('rt call', runner, queue, call) runner.do_call(call) @@ -82,10 +82,14 @@ class LocalhostMethodCallRouter(MethodCallRouterInterface): def main() -> int: with open('config.toml', 'rb') as fil: - config = from_toml(fil) + config = config_from_toml(fil) del config + with open('./examples/echoserver.toml', 'rb') as fil: + state = state_from_toml(fil) + print(state) + localhost_queue: Queue[MethodCall] = Queue() echo_client_queue: Queue[MethodCall] = Queue() echo_server_queue: Queue[MethodCall] = Queue() @@ -96,7 +100,7 @@ def main() -> int: localhost = LocalhostRunner() service_discovery.register_service(make_prelude(), localhost_queue) - with open('/home/johan/projects/idea/phasm/examples/echoserver.wasm', 'rb') as fil: + with open('./examples/echoserver.wasm', 'rb') as fil: echo_server = WasmTimeRunner(service_discovery, method_call_router, fil.read()) service_discovery.register_service(Service('echoserver', [ Method('echo', [ @@ -104,7 +108,7 @@ def main() -> int: ], valuetype.bytes) ]), echo_server_queue) - with open('/home/johan/projects/idea/phasm/examples/echoclient.wasm', 'rb') as fil: + with open('./examples/echoclient.wasm', 'rb') as fil: echo_client = WasmTimeRunner(service_discovery, method_call_router, fil.read()) # service_discovery.register_service(echo_client, echo_client_queue) diff --git a/phasmplatform/worker/runners/base.py b/phasmplatform/worker/runners/base.py index a78c442..f83548f 100644 --- a/phasmplatform/worker/runners/base.py +++ b/phasmplatform/worker/runners/base.py @@ -67,9 +67,6 @@ class BaseRunner(RunnerInterface): raise NotImplementedError(value_type, val) - def log_bytes(self, msg_ptr: int) -> None: - print('LOG: ' + self.read_bytes(msg_ptr).decode()) - def dump_memory(textio: TextIO, mem: bytes) -> None: line_width = 16 diff --git a/phasmplatform/worker/runners/wasmtime.py b/phasmplatform/worker/runners/wasmtime.py index 7180020..96ca38e 100644 --- a/phasmplatform/worker/runners/wasmtime.py +++ b/phasmplatform/worker/runners/wasmtime.py @@ -32,12 +32,6 @@ class WasmTimeRunner(BaseRunner): self.store = wasmtime.Store() self.module = wasmtime.Module(self.store.engine, wasm_bin) - from typing import Any - - def dump_args(*args: Any, **kwargs: Any) -> None: - print('args', args) - print('kwargs', kwargs) - imports: List[wasmtime.Func] = [] for imprt in self.module.imports: service = service_discovery.find_service(imprt.module) @@ -125,13 +119,11 @@ class WasmTimeRunner(BaseRunner): queue: Queue[Value] = Queue(maxsize=1) def on_success(val: Value) -> None: - print('hi mom') queue.put(val) def on_error(err: MethodCallError) -> None: print('Error while calling', service, method, args) - print('on_success', on_success) call = MethodCall(method, call_args, on_success, on_error) print(