Skip to content

Commit

Permalink
Merge pull request hyperledger#2230 from usingtechnology/feature/play…
Browse files Browse the repository at this point in the history
…ground-demo

stand up multiple agents (single and multi) for local development and testing
  • Loading branch information
swcurran committed May 17, 2023
2 parents 76240b8 + c8a220c commit a848bcb
Show file tree
Hide file tree
Showing 14 changed files with 1,212 additions and 1 deletion.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ Temporary Items
###

.idea/*
**/.idea/*

###
### Windows
Expand Down Expand Up @@ -188,4 +189,8 @@ _build/
**/*.iml

# Open API build
open-api/.build
open-api/.build

# poetry
poetry.lock
pyproject.toml
147 changes: 147 additions & 0 deletions demo/playground/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
NGROK_AUTHTOKEN=

ACAPY_GENESIS_URL=http://test.bcovrin.vonx.io/genesis

#
# database
#
POSTGRESQL_HOST=wallet-db
POSTGRESQL_PORT=5432
POSTGRESQL_USER=postgres
POSTGRESQL_PASSWORD=development
POSTGRESQL_ADMIN_USER=postgres
POSTGRESQL_ADMIN_PASSWORD=development

#
# wallet storage configuration
#
ACAPY_WALLET_STORAGE_CONFIG={"url":"${POSTGRESQL_HOST}:${POSTGRESQL_PORT}","wallet_scheme":"DatabasePerWallet"}
ACAPY_WALLET_STORAGE_CREDS={"account":"${POSTGRESQL_USER}","password":"${POSTGRESQL_PASSWORD}","admin_account":"${POSTGRESQL_ADMIN_USER}","admin_password":"${POSTGRESQL_ADMIN_PASSWORD}"}

#
# logging
#
ACAPY_LOG_LEVEL=INFO
RUST_LOG=ERROR

#
# tracing - uncomment in each service if needed
#
ACAPY_TRACE=0
ACAPY_TRACE_TARGET=http://logstash:9700/
ACAPY_TRACE_TAG=acapy.events
ACAPY_TRACE_LABEL=agent.trace

#
# NGROK Tunnels/Hosts : ensure services and tunnel config match if you change service ports
# tunnels:
# <XXX_TUNNEL_NAME>:
# proto: http
# addr: <XXX_HOST>:<XXX_AGENT_HTTP_IN_PORT>
#
NGROK_FABER_ALICE_HOST=ngrok-faber-alice
NGROK_ACME_MULTI_HOST=ngrok-acme-multi

#
# Faber : faber-agent 9001/9011
#
FABER_TUNNEL_HOST=${NGROK_FABER_ALICE_HOST}
FABER_TUNNEL_NAME=faber
FABER_HOST=faber-agent
FABER_AGENT_LABEL=faber
FABER_AGENT_HTTP_IN_PORT=9001
FABER_AGENT_HTTP_ADMIN_PORT=9011
FABER_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml
FABER_ACAPY_ENDPOINT=http://${FABER_HOST}:${FABER_AGENT_HTTP_IN_PORT}
FABER_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG}
FABER_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS}
FABER_ACAPY_WALLET_NAME=faber_wallet
FABER_ACAPY_WALLET_KEY=changeme
FABER_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL}
FABER_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL}
FABER_RUST_LOG=${RUST_LOG}
FABER_POSTGRESQL_HOST=${POSTGRESQL_HOST}
FABER_POSTGRESQL_PORT=${POSTGRESQL_PORT}
# tracing...
FABER_ACAPY_TRACE=${ACAPY_TRACE}
FABER_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET}
FABER_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG}
FABER_ACAPY_TRACE_LABEL=faber.agent.trace

#
# Alice : alice-agent - 9002/9012
#
ALICE_TUNNEL_HOST=${NGROK_FABER_ALICE_HOST}
ALICE_TUNNEL_NAME=alice
ALICE_HOST=alice-agent
ALICE_AGENT_LABEL=alice
ALICE_AGENT_HTTP_IN_PORT=9002
ALICE_AGENT_HTTP_ADMIN_PORT=9012
ALICE_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml
ALICE_ACAPY_ENDPOINT=http://${ALICE_HOST}:${ALICE_AGENT_HTTP_IN_PORT}
ALICE_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG}
ALICE_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS}
ALICE_ACAPY_WALLET_NAME=alice_wallet
ALICE_ACAPY_WALLET_KEY=changeme
ALICE_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL}
ALICE_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL}
ALICE_RUST_LOG=${RUST_LOG}
ALICE_POSTGRESQL_HOST=${POSTGRESQL_HOST}
ALICE_POSTGRESQL_PORT=${POSTGRESQL_PORT}
# tracing...
ALICE_ACAPY_TRACE=${ACAPY_TRACE}
ALICE_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET}
ALICE_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG}
ALICE_ACAPY_TRACE_LABEL=alice.agent.trace

#
# Acme : acme-agent - 9003/9013
#
ACME_TUNNEL_HOST=${NGROK_ACME_MULTI_HOST}
ACME_TUNNEL_NAME=acme
ACME_HOST=acme-agent
ACME_AGENT_LABEL=acme
ACME_AGENT_HTTP_IN_PORT=9003
ACME_AGENT_HTTP_ADMIN_PORT=9013
ACME_AGENT_ARG_FILE=./configs/singletenant-auto-accept.yml
ACME_ACAPY_ENDPOINT=http://${ACME_HOST}:${ACME_AGENT_HTTP_IN_PORT}
ACME_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG}
ACME_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS}
ACME_ACAPY_WALLET_NAME=acme_wallet
ACME_ACAPY_WALLET_KEY=changeme
ACME_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL}
ACME_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL}
ACME_RUST_LOG=${RUST_LOG}
ACME_POSTGRESQL_HOST=${POSTGRESQL_HOST}
ACME_POSTGRESQL_PORT=${POSTGRESQL_PORT}
# tracing...
ACME_ACAPY_TRACE=${ACAPY_TRACE}
ACME_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET}
ACME_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG}
ACME_ACAPY_TRACE_LABEL=acme.agent.trace

#
# Multi : multi-agent - 9004/9014
#
MULTI_TUNNEL_HOST=${NGROK_ACME_MULTI_HOST}
MULTI_TUNNEL_NAME=multi
MULTI_HOST=multi-agent
MULTI_AGENT_LABEL=multi
MULTI_AGENT_HTTP_IN_PORT=9004
MULTI_AGENT_HTTP_ADMIN_PORT=9014
MULTI_AGENT_ARG_FILE=./configs/multitenant-auto-accept.yml
MULTI_ACAPY_ENDPOINT=http://${MULTI_HOST}:${MULTI_AGENT_HTTP_IN_PORT}
MULTI_ACAPY_WALLET_STORAGE_CONFIG=${ACAPY_WALLET_STORAGE_CONFIG}
MULTI_ACAPY_WALLET_STORAGE_CREDS=${ACAPY_WALLET_STORAGE_CREDS}
MULTI_ACAPY_WALLET_NAME=multi_wallet
MULTI_ACAPY_WALLET_KEY=changeme
MULTI_ACAPY_GENESIS_URL=${ACAPY_GENESIS_URL}
MULTI_ACAPY_LOG_LEVEL=${ACAPY_LOG_LEVEL}
MULTI_RUST_LOG=${RUST_LOG}
MULTI_POSTGRESQL_HOST=${POSTGRESQL_HOST}
MULTI_POSTGRESQL_PORT=${POSTGRESQL_PORT}
# tracing...
MULTI_ACAPY_TRACE=${ACAPY_TRACE}
MULTI_ACAPY_TRACE_TARGET=${ACAPY_TRACE_TARGET}
MULTI_ACAPY_TRACE_TAG=${ACAPY_TRACE_TAG}
MULTI_ACAPY_TRACE_LABEL=multi.agent.trace
20 changes: 20 additions & 0 deletions demo/playground/Dockerfile.acapy
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1

USER root

RUN mkdir -p /acapy-agent
WORKDIR /acapy-agent

ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 /usr/bin/jq
RUN chmod +x /usr/bin/jq

USER $user

# Copy the necessary files
COPY ./start.sh start.sh
COPY ./configs configs

RUN chmod +x start.sh && \
aca-py --version > ./acapy-version.txt

ENTRYPOINT ["bash", "./start.sh"]
147 changes: 147 additions & 0 deletions demo/playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# ACA-Py Playground

This directory contains scripts to run several ACA-Py agents in various configurations for demonstration, development and testing scenarios. The agents are using Postgres (15) database storage (`askar`, `DatabasePerWallet`) and are running without security (`--admin-insecure-mode`).

The inspiration for this playground was testing mediation and the differences between single-tenant and multi-tenant modes. These scripts allowed the developer to stand up 3 single-tenant agents and 1 multi-tenant agent and run various scenarios (see [scripts](./scripts) - for some basic examples). Running in `--admin-insecure-mode` simplifies creating tenants in multi-tenant mode and eliminates the need for adding headers for calls in single-tenant mode.

- faber-agent
- alice-agent
- acme-agent
- multi-agent (the multi-tenant agent)

By default, all the agents share the same Postgres Database Service (version 15) and all use [Ngrok](https://ngrok.com) for publicly accessible URLs.

## Dependencies

Docker Compose version v2.17.2

## Agent Configuration

There are two simple configurations provided:

- [`singletenant-auto-accept.yml`](./configs/singletenant-auto-accept.yml)
- [`multitenant-auto-accept.yml`](./configs/multitenant-auto-accept.yml)

These configuration files are provided to the ACA-Py start command via the `AGENT_ARG_FILE` environment variable. See [`.env`](./.env.sample) and [`start.sh`](./start.sh).

### Dockerfile and start.sh

[`Dockerfile.acapy`](./Dockerfile.acapy) assembles the image to run. Currently based on [Aries Cloudagent Python 0.8.1](ghcr.io/hyperledger/aries-cloudagent-python:py3.9-indy-1.16.0-0.8.1), we need [jq](https://stedolan.github.io/jq/) to setup (or not) the ngrok tunnel and execute the Aca-py start command - see [`start.sh`](./start.sh). You may note that the start command is very sparse, additional configuration is done via environment variables in the [docker compose file](./docker-compose.yml).

### ngrok

Note that ngrok allows 2 tunnels per instance with an unpaid account. We have broken up the 4 default services into 2 ngrok services and tunnel configurations. If you need to alter port numbers for your agent services, you will have to update the ngrok tunnel files.

- [ngrok-faber-alice](./ngrok-faber-alice.yml)
- [ngrok-acme-multi](./ngrok-acme-multi.yml)

If you have a paid account, you can set the `NGROK_AUTHTOKEN` environment variable. See below.

### .env

Additional configuration (ie. port numbers for the services, `NGROK_AUTHTOKEN`, ...) are done in the [`.env`](./.env.sample) file. Change as needed and ensure ngrok configuration matches.

```shell
cp .env.sample .env
```

## Running the Playground

To run the agents in this repo, open a command shell in this directory and run:

- to build the containers:

```bash
docker compose build
```

- to run the agents:

```bash
docker compose up
```

- to shut down:

```bash
docker compose stop
docker compose rm -f
```

This will leave the agents wallet data, so if you restart the agent it will maintain any created data.

- to remove the wallet data:

```bash
docker compose down -v --remove-orphans
```

- individual services can be started by specifying the service name(s):

```bash
docker compose up multi-agent
docker compose up faber-agent alice-agent
```

You can now access the agent Admin APIs via Swagger at:

- faber: [http://localhost:9011/api/doc#](http://localhost:9011/api/doc#)
- alice: [http://localhost:9012/api/doc#](http://localhost:9012/api/doc#)
- acme: [http://localhost:9013/api/doc#](http://localhost:9013/api/doc#)
- multi: [http://localhost:9014/api/doc#](http://localhost:9014/api/doc#)

## Scripts

While having the Swagger Admin API is excellent, you may need to do something more complex than a single API call. You may need to see how agents with varying capabilities interact or validate that single-tenant and multi-tenant work the same. Jumping around from multiple browser tabs and cutting and pasting ids and JSON blocks can quickly grow tiresome.

A few Python (3.9) [scripts](./scripts) are provided as examples of what you may do once your agents are up and running.

```shell
cd scripts
pip install -r requirements.txt
python ping_agents.py
```

The [`ping_agents`](./scripts/ping_agents.py) script is a trivial example using the ACA-Py API to create tenants in the multi-agent instance and interact between the agents. We create and receive invitations and ping each other.

The [`mediator_ping_agents`](./scripts/mediator_ping_agents) script requires that you have a mediator service running and have the mediator's invitation URL. See [Aries Mediator Service](https://github.com/hyperledger/aries-mediator-service) for standing up a local instance and how to find the invitation URL. In this script, each agent requests mediation and we can see the mediator forwarding messages between the agents.

## Run without NGrok

[Ngrok](https://ngrok.com) provides a tunneling service and a way to provide a public IP to your locally running instance of ACA-Py. There are restrictions with Ngrok most notably regarding inbound connections.

```shell
Too many connections! The tunnel session SESSION has violated the rate-limit policy of THRESHOLD connections per minute by initiating COUNT connections in the last SECONDS seconds. Please decrease your inbound connection volume or upgrade to a paid plan for additional capacity.

ngrok limits the number of inbound connections to your tunnels. Limits are imposed on connections, not requests. If your HTTP clients use persistent connections aka HTTP keep-alive (most modern ones do), you'll likely never hit this limit. ngrok will return a 429 response to HTTP connections that exceed the rate limit. Connections to TCP and TLS tunnels violating the rate limit will be closed without a response.
```
If you do not require external access to your instance, consider turning NGrok off. NGrok tunnelling can be disabled by changing an environment variable for each service. Set `TUNNEL_NAME` to null/empty, and no tunnel will be created. See [`.env`](.env.sample), [`docker-compose.yml`](./docker-compose.yml) and [`start.sh`](./start.sh),
### update .env
```shell
FABER_TUNNEL_NAME=
```
### docker-compose.yml
```shell
environment:
- TUNNEL_HOST=${FABER_TUNNEL_HOST}
```
### start.sh
```shell
# if $TUNNEL_NAME is not empty, grab the service's ngrok route and set our ACAPY_ENDPOINT
if [[ ! -z "$TUNNEL_NAME" ]]; then . . .
```
Set service value to empty in `.env` that will set the `TUNNEL_NAME` environment variable to empty which will circumvent the use of the tunnel for the service (`ACAPY_ENDPOINT`).
## ELK Stack / Tracing logging
Please see [ELK Stack Readme](../elk-stack/README.md).
You may notice a series of environment variables for each agent service in [.env](./.env.sample) and commented-out network and agent configuration in the [docker compose file](./docker-compose.yml). Check the environment variables and uncomment as needed if wanting to send trace events to ELK.
10 changes: 10 additions & 0 deletions demo/playground/configs/multitenant-auto-accept.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Connections
debug-connections: true
auto-accept-invites: true
auto-accept-requests: true
auto-ping-connection: true

# multitenant
multitenant: true
multitenant-admin: true
jwt-secret: changeme
5 changes: 5 additions & 0 deletions demo/playground/configs/singletenant-auto-accept.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Connections
debug-connections: true
auto-accept-invites: true
auto-accept-requests: true
auto-ping-connection: true
Loading

0 comments on commit a848bcb

Please sign in to comment.