From 9c4805f419304f5b8f1e0402ba1aa7b2b645679c Mon Sep 17 00:00:00 2001 From: jamshale <31809382+jamshale@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:55:41 -0800 Subject: [PATCH] Add Multi-tenancy single wallet upgrade test (#3457) Signed-off-by: jamshale --- .../simple_restart/docker-compose.yml | 40 ++++ scenarios/examples/simple_restart/example.py | 171 +++++++++++++++--- 2 files changed, 182 insertions(+), 29 deletions(-) diff --git a/scenarios/examples/simple_restart/docker-compose.yml b/scenarios/examples/simple_restart/docker-compose.yml index c3c589c42d..59a7ed2d28 100644 --- a/scenarios/examples/simple_restart/docker-compose.yml +++ b/scenarios/examples/simple_restart/docker-compose.yml @@ -90,6 +90,43 @@ services: wallet-db: condition: service_healthy + agency: + image: bcgovimages/aries-cloudagent:py3.12_1.0.1 + ports: + - "3003:3001" + command: > + start + --label Agency + --inbound-transport http 0.0.0.0 3000 + --outbound-transport http + --endpoint http://agency:3000 + --admin 0.0.0.0 3001 + --admin-insecure-mode + --tails-server-base-url http://tails:6543 + --genesis-url http://test.bcovrin.vonx.io/genesis + --wallet-type askar + --wallet-name agency + --wallet-key insecure + --auto-provision + --multitenant + --multitenant-admin + --jwt-secret insecure + --multitenancy-config wallet_type=single-wallet-askar key_derivation_method=RAW + --wallet-storage-type "postgres_storage" + --wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5}" + --wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}" + --log-level info + --debug-webhooks + healthcheck: + test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null + start_period: 30s + interval: 7s + timeout: 5s + retries: 5 + depends_on: + tails: + condition: service_started + tails: image: ghcr.io/bcgov/tails-server:latest ports: @@ -112,6 +149,7 @@ services: - DOCKER_HOST=unix:///var/run/docker.sock - ALICE=http://alice:3001 - BOB=http://bob:3001 + - AGENCY=http://agency:3001 volumes: - /var/run/docker.sock:/var/run/docker.sock - ./example.py:/usr/src/app/example.py:ro,z @@ -121,3 +159,5 @@ services: condition: service_healthy bob: condition: service_healthy + agency: + condition: service_healthy diff --git a/scenarios/examples/simple_restart/example.py b/scenarios/examples/simple_restart/example.py index 7737842360..17597dce6b 100644 --- a/scenarios/examples/simple_restart/example.py +++ b/scenarios/examples/simple_restart/example.py @@ -5,40 +5,50 @@ import asyncio from os import getenv -import json -import time - -import docker -from docker.errors import NotFound -from docker.models.containers import Container from acapy_controller import Controller from acapy_controller.logging import logging_to_stdout +from acapy_controller.models import CreateWalletResponse from acapy_controller.protocols import ( - connection, didexchange, indy_anoncred_credential_artifacts, indy_anoncred_onboard, - indy_anoncreds_publish_revocation, - indy_anoncreds_revoke, indy_issue_credential_v2, indy_present_proof_v2, ) - from examples.util import ( - healthy, - unhealthy, wait_until_healthy, ) +import docker ALICE = getenv("ALICE", "http://alice:3001") BOB = getenv("BOB", "http://bob:3001") +AGENCY = getenv("AGENCY", "http://agency:3001") async def main(): """Test Controller protocols.""" - async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob: + # create multitenant issuer tenant + async with Controller(base_url=AGENCY) as agency: + multitenant_issuer_tenant = await agency.post( + "/multitenancy/wallet", + json={ + "label": "MultitenantIssuer", + "wallet_type": "askar", + }, + response=CreateWalletResponse, + ) + + async with ( + Controller(base_url=ALICE) as alice, + Controller(base_url=BOB) as bob, + Controller( + base_url=AGENCY, + wallet_id=multitenant_issuer_tenant.wallet_id, + subwallet_token=multitenant_issuer_tenant.token, + ) as multitenant_issuer, + ): # connect the 2 agents print(">>> connecting agents ...") (alice_conn, bob_conn) = await didexchange(alice, bob) @@ -74,45 +84,100 @@ async def main(): ) print(">>> Done!") + # connect multitenant issuer to bob + print(">>> connecting agents ...") + (multitenant_issuer_conn, bob_to_mt_conn) = await didexchange( + multitenant_issuer, bob + ) + + # setup multitenant issuer as an issuer + print(">>> setting up multitenant issuer as issuer ...") + await indy_anoncred_onboard(multitenant_issuer) + schema, cred_def = await indy_anoncred_credential_artifacts( + multitenant_issuer, + ["firstname", "lastname"], + support_revocation=True, + ) + + # Issue a credential + print(">>> issue credential ...") + multitenant_issuer_cred_ex, _ = await indy_issue_credential_v2( + multitenant_issuer, + bob, + multitenant_issuer_conn.connection_id, + bob_to_mt_conn.connection_id, + cred_def.credential_definition_id, + {"firstname": "Bob", "lastname": "Builder"}, + ) + + # Present the the credential's attributes + print(">>> present proof ...") + await indy_present_proof_v2( + bob, + multitenant_issuer, + bob_to_mt_conn.connection_id, + multitenant_issuer_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + print(">>> Done!") + # play with docker client = docker.from_env() containers = client.containers.list(all=True) docker_containers = {} for container in containers: - if 'com.docker.compose.service' in container.attrs['Config']['Labels']: - container_name = container.attrs['Config']['Labels']['com.docker.compose.service'] - container_id = container.attrs['Id'] - container_is_running = container.attrs['State']['Running'] - docker_containers[container_name] = {'Id': container_id, 'Running': container_is_running} + if "com.docker.compose.service" in container.attrs["Config"]["Labels"]: + container_name = container.attrs["Config"]["Labels"][ + "com.docker.compose.service" + ] + container_id = container.attrs["Id"] + container_is_running = container.attrs["State"]["Running"] + docker_containers[container_name] = { + "Id": container_id, + "Running": container_is_running, + } print(">>> container:", container_name, docker_containers[container_name]) # try to restart a container (stop alice and start alice-upgrade) - alice_docker_container = docker_containers['alice'] - alice_container = client.containers.get(alice_docker_container['Id']) + alice_docker_container = docker_containers["alice"] + alice_container = client.containers.get(alice_docker_container["Id"]) + + # try to restart agency container (stop agency and start agency-upgrade) + agency_docker_container = docker_containers["agency"] + agency_container = client.containers.get(agency_docker_container["Id"]) print(">>> shut down alice ...") alice_container.stop() + print(">>> shut down agency ...") + agency_container.stop() + print(">>> waiting for alice container to exit ...") - alice_id = alice_container.attrs['Id'] + alice_id = agency_container.attrs["Id"] wait_until_healthy(client, alice_id, is_healthy=False) alice_container.remove() + print(">>> waiting for agency container to exit ...") + agency_id = agency_container.attrs["Id"] + wait_until_healthy(client, agency_id, is_healthy=False) + agency_container.remove() + + # Upgrade alice and perform some tests new_alice_container = None alice_id = None try: print(">>> start new alice container ...") new_alice_container = client.containers.run( - 'acapy-test', - command=alice_container.attrs['Config']['Cmd'], + "acapy-test", + command=alice_container.attrs["Config"]["Cmd"], detach=True, - environment={'RUST_LOG': 'aries-askar::log::target=error'}, - healthcheck=alice_container.attrs['Config']['Healthcheck'], - name='alice', - network=alice_container.attrs['HostConfig']['NetworkMode'], - ports=alice_container.attrs['NetworkSettings']['Ports'], + environment={"RUST_LOG": "aries-askar::log::target=error"}, + healthcheck=alice_container.attrs["Config"]["Healthcheck"], + name="alice", + network=alice_container.attrs["HostConfig"]["NetworkMode"], + ports=alice_container.attrs["NetworkSettings"]["Ports"], ) - alice_id = new_alice_container.attrs['Id'] + alice_id = new_alice_container.attrs["Id"] wait_until_healthy(client, alice_id) print(">>> new alice container is healthy") @@ -138,6 +203,54 @@ async def main(): wait_until_healthy(client, alice_id, is_healthy=False) alice_container.remove() + # Upgrade agency and perform some tests + new_agency_container = None + agency_id = None + try: + print(">>> start new agency container ...") + new_agency_container = client.containers.run( + "acapy-test", + command=agency_container.attrs["Config"]["Cmd"], + detach=True, + environment={"RUST_LOG": "aries-askar::log::target=error"}, + healthcheck=agency_container.attrs["Config"]["Healthcheck"], + name="agency", + network=agency_container.attrs["HostConfig"]["NetworkMode"], + ports=agency_container.attrs["NetworkSettings"]["Ports"], + ) + agency_id = new_agency_container.attrs["Id"] + + wait_until_healthy(client, agency_id) + print(">>> new agency container is healthy") + + # run some more tests ... agency tenant should still be connected to bob ... + async with ( + Controller(base_url=BOB) as bob, + Controller( + base_url=AGENCY, + wallet_id=multitenant_issuer_tenant.wallet_id, + subwallet_token=multitenant_issuer_tenant.token, + ) as multitenant_issuer, + ): + # Present the the credential's attributes + print(">>> present proof ... again ...") + await indy_present_proof_v2( + bob, + multitenant_issuer, + bob_to_mt_conn.connection_id, + multitenant_issuer_conn.connection_id, + requested_attributes=[{"name": "firstname"}], + ) + print(">>> Done! (again)") + finally: + if agency_id and new_agency_container: + # cleanup - shut down agency agent (not part of docker compose) + print(">>> shut down agency ...") + agency_container = client.containers.get(agency_id) + agency_container.stop() + wait_until_healthy(client, agency_id, is_healthy=False) + agency_container.remove() + if __name__ == "__main__": logging_to_stdout()