diff --git a/conftest.py b/conftest.py index 312d77220a..a46dd600bc 100644 --- a/conftest.py +++ b/conftest.py @@ -20,9 +20,11 @@ def pytest_sessionstart(session): INDY_FOUND = True except ImportError: - "skipping Indy-specific tests: python module not installed", + print("Skipping Indy-specific tests: python3-indy module not installed.") except OSError: - "skipping Indy-specific tests: shared library not loaded", + print( + "Skipping Indy-specific tests: libindy shared library could not be loaded." + ) if not INDY_FOUND: modules = {} diff --git a/demo/__init__.py b/demo/__init__.py deleted file mode 100644 index 00075b4ded..0000000000 --- a/demo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Cloud agent demo scripts and utilities.""" diff --git a/demo/run_demo b/demo/run_demo index 12bf8e9739..48b55461d9 100755 --- a/demo/run_demo +++ b/demo/run_demo @@ -4,43 +4,42 @@ shopt -s nocasematch cd $(dirname $0) -AGENT=$1 -if [[ $AGENT == "faber" ]] || [[ $AGENT == "alice" ]] || [[ $AGENT == "acme" ]] || [[ $AGENT == "performance" ]]; then - echo "Preparing agent image..." - docker build -q -t faber-alice-demo -f ../docker/Dockerfile.demo .. || exit 1 -else - echo "Please specify which agent you want to run. Choose from 'faber', 'alice', 'acme', or 'performance'."; - exit 1; -fi +AGENT="$1" +shift -echo "Starting $AGENT..." -if [[ $AGENT == "faber" ]]; then +if [ "$AGENT" = "faber" ]; then AGENT_MODULE="faber" AGENT_PORT=8020 AGENT_PORT_RANGE=8020-8027 -elif [[ $AGENT == "alice" ]]; then +elif [ "$AGENT" = "alice" ]; then AGENT_MODULE="alice" AGENT_PORT=8030 AGENT_PORT_RANGE=8030-8037 -elif [[ $AGENT == "acme" ]]; then +elif [ "$AGENT" = "acme" ]; then AGENT_MODULE="acme" AGENT_PORT=8040 AGENT_PORT_RANGE=8040-8047 -else +elif [ "$AGENT" = "performance" ]; then AGENT_MODULE="performance" AGENT_PORT=8030 AGENT_PORT_RANGE=8030-8038 +else + echo "Please specify which agent you want to run. Choose from 'faber', 'alice', 'acme', or 'performance'." + exit 1 fi -if [[ -z "${PWD_HOST_FQDN}" ]]; then +echo "Preparing agent image..." +docker build -q -t faber-alice-demo -f ../docker/Dockerfile.demo .. || exit 1 + +if [ -z "${PWD_HOST_FQDN}" ]; then DOCKERHOST=`docker run --rm --net=host codenvy/che-ip` export RUNMODE="docker" else PWD_HOST="${PWD_HOST_FQDN}" - if [ $PWD_HOST_FQDN == "labs.play-with-docker.com" ] + if [ "$PWD_HOST_FQDN" = "labs.play-with-docker.com" ] then export ETH_CONFIG="eth1" - elif [ $PWD_HOST_FQDN == "play-with-docker.vonx.io" ] + elif [ "$PWD_HOST_FQDN" = "play-with-docker.vonx.io" ] then export ETH_CONFIG="eth0" else @@ -63,12 +62,13 @@ if ! [ -z "$LEDGER_URL" ]; then fi # on Windows, docker run needs to be prefixed by winpty -if [[ "$OSTYPE" == "msys" ]]; then +if [ "$OSTYPE" = "msys" ]; then DOCKER="winpty docker" fi DOCKER=${DOCKER:-docker} +echo "Starting $AGENT..." $DOCKER run --name $AGENT --rm -it \ -p 0.0.0.0:$AGENT_PORT_RANGE:$AGENT_PORT_RANGE \ $DOCKER_ENV \ - faber-alice-demo $AGENT_MODULE $AGENT_PORT + faber-alice-demo $AGENT_MODULE --port $AGENT_PORT $@ diff --git a/demo/runners/__init__.py b/demo/runners/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/demo/acme.py b/demo/runners/acme.py similarity index 75% rename from demo/acme.py rename to demo/runners/acme.py index 9d44d003c6..e1cc79274f 100644 --- a/demo/acme.py +++ b/demo/runners/acme.py @@ -2,18 +2,21 @@ import json import logging import os -import random import sys -from .agent import DemoAgent, default_genesis_txns -from .utils import log_json, log_msg, log_status, log_timer, prompt, prompt_loop +from .support.agent import DemoAgent, default_genesis_txns +from .support.utils import ( + log_json, + log_msg, + log_status, + log_timer, + prompt, + prompt_loop, + require_indy, +) LOGGER = logging.getLogger(__name__) -AGENT_PORT = int(sys.argv[1]) - -TIMING = False - class AcmeAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): @@ -21,7 +24,8 @@ def __init__(self, http_port: int, admin_port: int, **kwargs): self.connection_id = None self._connection_ready = asyncio.Future() self.cred_state = {} - # TODO define a dict to hold credential attributes based on credential_definition_id + # TODO define a dict to hold credential attributes based on + # the credential_definition_id self.cred_attrs = {} async def detect_connection(self): @@ -75,7 +79,7 @@ async def handle_basicmessages(self, message): self.log("Received message:", message["content"]) -async def main(): +async def main(start_port: int, show_timing: bool = False): genesis = await default_genesis_txns() if not genesis: @@ -83,11 +87,12 @@ async def main(): sys.exit(1) agent = None - start_port = AGENT_PORT try: log_status("#1 Provision an agent and wallet, get back configuration details") - agent = AcmeAgent(start_port, start_port + 1, genesis_data=genesis) + agent = AcmeAgent( + start_port, start_port + 1, genesis_data=genesis, timing=show_timing + ) await agent.listen_webhooks(start_port + 2) await agent.register_did() @@ -99,18 +104,24 @@ async def main(): # Create a schema log_status("#3 Create a new schema on the ledger") with log_timer("Publish schema duration:"): - version = format( - "%d.%d.%d" - % ( - random.randint(1, 101), - random.randint(1, 101), - random.randint(1, 101), - ) - ) + pass # TODO define schema - #(schema_id, credential_definition_id) = await agent.register_schema_and_creddef( - # "employee id schema", version, ["employee_id", "name", "date", "position"] - # ) + # version = format( + # "%d.%d.%d" + # % ( + # random.randint(1, 101), + # random.randint(1, 101), + # random.randint(1, 101), + # ) + # ) + # ( + # schema_id, + # credential_definition_id, + # ) = await agent.register_schema_and_creddef( + # "employee id schema", + # version, + # ["employee_id", "name", "date", "position"], + # ) with log_timer("Generate invitation duration:"): # Generate an invitation @@ -138,12 +149,10 @@ async def main(): elif option == "1": log_status("#13 Issue credential offer to X") # TODO credential offers - elif option == "2": log_status("#20 Request proof of degree from alice") # TODO presentation requests - elif option == "3": msg = await prompt("Enter message: ") @@ -151,7 +160,7 @@ async def main(): f"/connections/{agent.connection_id}/send-message", {"content": msg} ) - if TIMING: + if show_timing: timing = await agent.fetch_timing() if timing: for line in agent.format_timing(timing): @@ -173,7 +182,25 @@ async def main(): if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Runs an Acme demo agent.") + parser.add_argument( + "-p", + "--port", + type=int, + default=8040, + metavar=(""), + help="Choose the starting port number to listen on", + ) + parser.add_argument( + "--timing", action="store_true", help="Enable timing information" + ) + args = parser.parse_args() + + require_indy() + try: - asyncio.get_event_loop().run_until_complete(main()) + asyncio.get_event_loop().run_until_complete(main(args.port, args.timing)) except KeyboardInterrupt: os._exit(1) diff --git a/demo/alice.py b/demo/runners/alice.py similarity index 88% rename from demo/alice.py rename to demo/runners/alice.py index c1ac8d92f5..d26753c775 100644 --- a/demo/alice.py +++ b/demo/runners/alice.py @@ -4,15 +4,19 @@ import os import sys -from .agent import DemoAgent, default_genesis_txns -from .utils import log_json, log_msg, log_status, log_timer, prompt, prompt_loop +from .support.agent import DemoAgent, default_genesis_txns +from .support.utils import ( + log_json, + log_msg, + log_status, + log_timer, + prompt, + prompt_loop, + require_indy, +) LOGGER = logging.getLogger(__name__) -AGENT_PORT = int(sys.argv[1]) - -TIMING = False - class AliceAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): @@ -163,7 +167,7 @@ async def input_invitation(agent): await agent.detect_connection() -async def main(): +async def main(start_port: int, show_timing: bool = False): genesis = await default_genesis_txns() if not genesis: @@ -171,11 +175,12 @@ async def main(): sys.exit(1) agent = None - start_port = AGENT_PORT try: log_status("#7 Provision an agent and wallet, get back configuration details") - agent = AliceAgent(start_port, start_port + 1, genesis_data=genesis) + agent = AliceAgent( + start_port, start_port + 1, genesis_data=genesis, timing=show_timing + ) await agent.listen_webhooks(start_port + 2) with log_timer("Startup duration:"): @@ -203,7 +208,7 @@ async def main(): log_status("Input new invitation details") await input_invitation(agent) - if TIMING: + if show_timing: timing = await agent.fetch_timing() if timing: for line in agent.format_timing(timing): @@ -225,7 +230,25 @@ async def main(): if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Runs an Alice demo agent.") + parser.add_argument( + "-p", + "--port", + type=int, + default=8030, + metavar=(""), + help="Choose the starting port number to listen on", + ) + parser.add_argument( + "--timing", action="store_true", help="Enable timing information" + ) + args = parser.parse_args() + + require_indy() + try: - asyncio.get_event_loop().run_until_complete(main()) + asyncio.get_event_loop().run_until_complete(main(args.port, args.timing)) except KeyboardInterrupt: os._exit(1) diff --git a/demo/faber.py b/demo/runners/faber.py similarity index 89% rename from demo/faber.py rename to demo/runners/faber.py index 0f25581ae8..508d8092bf 100644 --- a/demo/faber.py +++ b/demo/runners/faber.py @@ -5,15 +5,19 @@ import random import sys -from .agent import DemoAgent, default_genesis_txns -from .utils import log_json, log_msg, log_status, log_timer, prompt, prompt_loop +from .support.agent import DemoAgent, default_genesis_txns +from .support.utils import ( + log_json, + log_msg, + log_status, + log_timer, + prompt, + prompt_loop, + require_indy, +) LOGGER = logging.getLogger(__name__) -AGENT_PORT = int(sys.argv[1]) - -TIMING = False - class FaberAgent(DemoAgent): def __init__(self, http_port: int, admin_port: int, **kwargs): @@ -92,7 +96,7 @@ async def handle_basicmessages(self, message): self.log("Received message:", message["content"]) -async def main(): +async def main(start_port: int, show_timing: bool = False): genesis = await default_genesis_txns() if not genesis: @@ -100,11 +104,12 @@ async def main(): sys.exit(1) agent = None - start_port = AGENT_PORT try: log_status("#1 Provision an agent and wallet, get back configuration details") - agent = FaberAgent(start_port, start_port + 1, genesis_data=genesis) + agent = FaberAgent( + start_port, start_port + 1, genesis_data=genesis, timing=show_timing + ) await agent.listen_webhooks(start_port + 2) await agent.register_did() @@ -199,7 +204,7 @@ async def main(): f"/connections/{agent.connection_id}/send-message", {"content": msg} ) - if TIMING: + if show_timing: timing = await agent.fetch_timing() if timing: for line in agent.format_timing(timing): @@ -221,7 +226,25 @@ async def main(): if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Runs a Faber demo agent.") + parser.add_argument( + "-p", + "--port", + type=int, + default=8020, + metavar=(""), + help="Choose the starting port number to listen on", + ) + parser.add_argument( + "--timing", action="store_true", help="Enable timing information" + ) + args = parser.parse_args() + + require_indy() + try: - asyncio.get_event_loop().run_until_complete(main()) + asyncio.get_event_loop().run_until_complete(main(args.port, args.timing)) except KeyboardInterrupt: os._exit(1) diff --git a/demo/performance.py b/demo/runners/performance.py similarity index 88% rename from demo/performance.py rename to demo/runners/performance.py index a8b833ca10..3d86db5c5a 100644 --- a/demo/performance.py +++ b/demo/runners/performance.py @@ -4,31 +4,24 @@ import random import sys -from .agent import DemoAgent, default_genesis_txns -from .utils import log_timer, progress +from .support.agent import DemoAgent, default_genesis_txns +from .support.utils import log_timer, progress, require_indy LOGGER = logging.getLogger(__name__) -START_PORT = int(sys.argv[1]) - -ROUTING = False - -TIMING = True - class BaseAgent(DemoAgent): def __init__( self, ident: str, port: int, - timing: bool = TIMING, prefix: str = None, use_routing: bool = False, **kwargs, ): if prefix is None: prefix = ident - super().__init__(ident, port, port + 1, timing=timing, prefix=prefix, **kwargs) + super().__init__(ident, port, port + 1, prefix=prefix, **kwargs) self._connection_id = None self._connection_ready = None @@ -155,7 +148,7 @@ def __init__(self, port: int, **kwargs): super().__init__("Router", port, **kwargs) -async def main(): +async def main(start_port: int, show_timing: bool = False, routing: bool = False): genesis = await default_genesis_txns() if not genesis: @@ -169,17 +162,17 @@ async def main(): run_timer.start() try: - start_port = START_PORT - - alice = AliceAgent(start_port, genesis_data=genesis) + alice = AliceAgent(start_port, genesis_data=genesis, timing=show_timing) await alice.listen_webhooks(start_port + 2) - faber = FaberAgent(start_port + 3, genesis_data=genesis) + faber = FaberAgent(start_port + 3, genesis_data=genesis, timing=show_timing) await faber.listen_webhooks(start_port + 5) await faber.register_did() - if ROUTING: - alice_router = RoutingAgent(start_port + 6, genesis_data=genesis) + if routing: + alice_router = RoutingAgent( + start_port + 6, genesis_data=genesis, timing=show_timing + ) await alice_router.listen_webhooks(start_port + 8) await alice_router.register_did() @@ -193,14 +186,14 @@ async def main(): await faber.publish_defs() with log_timer("Connect duration:"): - if ROUTING: + if routing: router_invite = await alice_router.get_invite() alice_router_conn_id = await alice.receive_invite(router_invite) await asyncio.wait_for(alice.detect_connection(), 30) invite = await faber.get_invite() - if ROUTING: + if routing: conn_id = await alice.receive_invite(invite, accept="manual") await alice.establish_inbound(conn_id, alice_router_conn_id) await alice.accept_invite(conn_id) @@ -210,10 +203,10 @@ async def main(): await asyncio.wait_for(faber.detect_connection(), 30) - if TIMING: + if show_timing: await alice.reset_timing() await faber.reset_timing() - if ROUTING: + if routing: await alice_router.reset_timing() issue_count = 300 @@ -274,7 +267,7 @@ async def check_received(agent, issue_count, pb): avg = recv_timer.duration / issue_count alice.log(f"Average time per credential: {avg:.2f}s ({1/avg:.2f}/s)") - if TIMING: + if show_timing: timing = await alice.fetch_timing() if timing: for line in alice.format_timing(timing): @@ -284,7 +277,7 @@ async def check_received(agent, issue_count, pb): if timing: for line in faber.format_timing(timing): faber.log(line) - if ROUTING: + if routing: timing = await alice_router.fetch_timing() if timing: for line in alice_router.format_timing(timing): @@ -319,7 +312,27 @@ async def check_received(agent, issue_count, pb): if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="Runs an automated credential issuance performance demo." + ) + parser.add_argument( + "-p", + "--port", + type=int, + default=8030, + metavar=(""), + help="Choose the starting port number to listen on", + ) + parser.add_argument( + "--routing", action="store_true", help="Enable inbound routing demonstration" + ) + args = parser.parse_args() + + require_indy() + try: - asyncio.get_event_loop().run_until_complete(main()) + asyncio.get_event_loop().run_until_complete(main(args.port, True, args.routing)) except KeyboardInterrupt: os._exit(1) diff --git a/demo/runners/support/__init__.py b/demo/runners/support/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/demo/agent.py b/demo/runners/support/agent.py similarity index 100% rename from demo/agent.py rename to demo/runners/support/agent.py diff --git a/demo/utils.py b/demo/runners/support/utils.py similarity index 95% rename from demo/utils.py rename to demo/runners/support/utils.py index e016097400..e402d4f9a4 100644 --- a/demo/utils.py +++ b/demo/runners/support/utils.py @@ -1,6 +1,7 @@ import functools import json import os +import sys from timeit import default_timer import prompt_toolkit @@ -231,3 +232,16 @@ def log_timer(label: str, show: bool = True, logger=None, **kwargs): def progress(*args, **kwargs): return ProgressBar(*args, **kwargs) + + +def require_indy(): + try: + from indy.libindy import _cdll + + _cdll() + except ImportError: + print("python3-indy module not installed") + sys.exit(1) + except OSError: + print("libindy shared library could not be loaded") + sys.exit(1) diff --git a/docker/Dockerfile.demo b/docker/Dockerfile.demo index e4b7f5f69e..f4312ac8d5 100644 --- a/docker/Dockerfile.demo +++ b/docker/Dockerfile.demo @@ -24,4 +24,4 @@ RUN pip3 install --no-cache-dir -r demo/requirements.txt ADD demo ./demo -ENTRYPOINT ["/bin/bash", "-c", "python -m \"demo.$@\"", "--"] +ENTRYPOINT ["/bin/bash", "-c", "python -m demo.runners.$@", "--"]