Skip to content

Commit 969b9f9

Browse files
authored
Aca-Py test scenario including a container restart (with aca-py version upgrade) (openwallet-foundation#3400)
* Fix for demo initial cred_type override Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * Initial version of a scenario test with restart Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * WIP container restart example Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * Make docker network explicit Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * Add some agent activity to the restart test Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * Test cleanup Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> * Remove unused env var Signed-off-by: Ian Costanzo <ian@anon-solutions.ca> --------- Signed-off-by: Ian Costanzo <ian@anon-solutions.ca>
1 parent e3b0841 commit 969b9f9

File tree

4 files changed

+1104
-575
lines changed

4 files changed

+1104
-575
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
services:
2+
wallet-db:
3+
image: postgres:12
4+
environment:
5+
- POSTGRES_USER=DB_USER
6+
- POSTGRES_PASSWORD=DB_PASSWORD
7+
ports:
8+
- 5433:5432
9+
healthcheck:
10+
test: ["CMD-SHELL", "pg_isready -U DB_USER"]
11+
interval: 10s
12+
retries: 5
13+
start_period: 30s
14+
timeout: 10s
15+
16+
alice:
17+
image: bcgovimages/aries-cloudagent:py3.12_1.0.1
18+
ports:
19+
- "3001:3001"
20+
environment:
21+
RUST_LOG: 'aries-askar::log::target=error'
22+
command: >
23+
start
24+
--label Alice
25+
--inbound-transport http 0.0.0.0 3000
26+
--outbound-transport http
27+
--endpoint http://alice:3000
28+
--admin 0.0.0.0 3001
29+
--admin-insecure-mode
30+
--tails-server-base-url http://tails:6543
31+
--genesis-url http://test.bcovrin.vonx.io/genesis
32+
--wallet-type askar
33+
--wallet-name alice
34+
--wallet-key insecure
35+
--wallet-storage-type "postgres_storage"
36+
--wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5}"
37+
--wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}"
38+
--auto-provision
39+
--log-level debug
40+
--debug-webhooks
41+
--preserve-exchange-records
42+
healthcheck:
43+
test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null
44+
start_period: 30s
45+
interval: 7s
46+
timeout: 5s
47+
retries: 5
48+
depends_on:
49+
tails:
50+
condition: service_started
51+
wallet-db:
52+
condition: service_healthy
53+
54+
bob:
55+
image: acapy-test
56+
ports:
57+
- "3002:3001"
58+
environment:
59+
RUST_LOG: 'aries-askar::log::target=error'
60+
command: >
61+
start
62+
--label Bob
63+
--inbound-transport http 0.0.0.0 3000
64+
--outbound-transport http
65+
--endpoint http://bob:3000
66+
--admin 0.0.0.0 3001
67+
--admin-insecure-mode
68+
--tails-server-base-url http://tails:6543
69+
--genesis-url http://test.bcovrin.vonx.io/genesis
70+
--wallet-type askar
71+
--wallet-name bob
72+
--wallet-key insecure
73+
--wallet-storage-type "postgres_storage"
74+
--wallet-storage-config "{\"url\":\"wallet-db:5432\",\"max_connections\":5}"
75+
--wallet-storage-creds "{\"account\":\"DB_USER\",\"password\":\"DB_PASSWORD\",\"admin_account\":\"DB_USER\",\"admin_password\":\"DB_PASSWORD\"}"
76+
--auto-provision
77+
--log-level debug
78+
--debug-webhooks
79+
--monitor-revocation-notification
80+
--preserve-exchange-records
81+
healthcheck:
82+
test: curl -s -o /dev/null -w '%{http_code}' "http://localhost:3001/status/live" | grep "200" > /dev/null
83+
start_period: 30s
84+
interval: 7s
85+
timeout: 5s
86+
retries: 5
87+
depends_on:
88+
tails:
89+
condition: service_started
90+
wallet-db:
91+
condition: service_healthy
92+
93+
tails:
94+
image: ghcr.io/bcgov/tails-server:latest
95+
ports:
96+
- 6543:6543
97+
environment:
98+
- GENESIS_URL=http://test.bcovrin.vonx.io/genesis
99+
command: >
100+
tails-server
101+
--host 0.0.0.0
102+
--port 6543
103+
--storage-path /tmp/tails-files
104+
--log-level INFO
105+
106+
example:
107+
container_name: controller
108+
privileged: true
109+
build:
110+
context: ../..
111+
environment:
112+
- DOCKER_HOST=unix:///var/run/docker.sock
113+
- ALICE=http://alice:3001
114+
- BOB=http://bob:3001
115+
volumes:
116+
- /var/run/docker.sock:/var/run/docker.sock
117+
- ./example.py:/usr/src/app/example.py:ro,z
118+
command: python -m example
119+
depends_on:
120+
alice:
121+
condition: service_healthy
122+
bob:
123+
condition: service_healthy
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""Minimal reproducible example script.
2+
3+
This script is for you to use to reproduce a bug or demonstrate a feature.
4+
"""
5+
6+
import asyncio
7+
from os import getenv
8+
import json
9+
import time
10+
11+
import docker
12+
from docker.errors import NotFound
13+
from docker.models.containers import Container
14+
from docker.models.networks import Network
15+
16+
from acapy_controller import Controller
17+
from acapy_controller.logging import logging_to_stdout
18+
from acapy_controller.protocols import (
19+
connection,
20+
didexchange,
21+
indy_anoncred_credential_artifacts,
22+
indy_anoncred_onboard,
23+
indy_anoncreds_publish_revocation,
24+
indy_anoncreds_revoke,
25+
indy_issue_credential_v2,
26+
indy_present_proof_v2,
27+
)
28+
29+
ALICE = getenv("ALICE", "http://alice:3001")
30+
BOB = getenv("BOB", "http://bob:3001")
31+
32+
33+
def healthy(container: Container) -> bool:
34+
"""Check if container is healthy."""
35+
inspect_results = container.attrs
36+
return inspect_results["State"]["Running"] and inspect_results["State"]["Health"]["Status"] == "healthy"
37+
38+
39+
def unhealthy(container: Container) -> bool:
40+
"""Check if container is unhealthy."""
41+
inspect_results = container.attrs
42+
return not inspect_results["State"]["Running"]
43+
44+
45+
def wait_until_healthy(client, container_id: str, attempts: int = 350, is_healthy=True):
46+
"""Wait until container is healthy."""
47+
container = client.containers.get(container_id)
48+
print((container.name, container.status))
49+
for _ in range(attempts):
50+
if (is_healthy and healthy(container)) or unhealthy(container):
51+
return
52+
else:
53+
time.sleep(1)
54+
container = client.containers.get(container_id)
55+
raise TimeoutError("Timed out waiting for container")
56+
57+
58+
async def main():
59+
"""Test Controller protocols."""
60+
async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob:
61+
# connect the 2 agents
62+
print(">>> connecting agents ...")
63+
(alice_conn, bob_conn) = await didexchange(alice, bob)
64+
65+
# setup alice as an issuer
66+
print(">>> setting up alice as issuer ...")
67+
await indy_anoncred_onboard(alice)
68+
schema, cred_def = await indy_anoncred_credential_artifacts(
69+
alice,
70+
["firstname", "lastname"],
71+
support_revocation=True,
72+
)
73+
74+
# Issue a credential
75+
print(">>> issue credential ...")
76+
alice_cred_ex, _ = await indy_issue_credential_v2(
77+
alice,
78+
bob,
79+
alice_conn.connection_id,
80+
bob_conn.connection_id,
81+
cred_def.credential_definition_id,
82+
{"firstname": "Bob", "lastname": "Builder"},
83+
)
84+
85+
# Present the the credential's attributes
86+
print(">>> present proof ...")
87+
await indy_present_proof_v2(
88+
bob,
89+
alice,
90+
bob_conn.connection_id,
91+
alice_conn.connection_id,
92+
requested_attributes=[{"name": "firstname"}],
93+
)
94+
print(">>> Done!")
95+
96+
# play with docker
97+
client = docker.from_env()
98+
containers = client.containers.list(all=True)
99+
docker_containers = {}
100+
for container in containers:
101+
if 'com.docker.compose.service' in container.attrs['Config']['Labels']:
102+
container_name = container.attrs['Config']['Labels']['com.docker.compose.service']
103+
container_id = container.attrs['Id']
104+
container_is_running = container.attrs['State']['Running']
105+
docker_containers[container_name] = {'Id': container_id, 'Running': container_is_running}
106+
print(">>> container:", container_name, docker_containers[container_name])
107+
108+
# try to restart a container (stop alice and start alice-upgrade)
109+
alice_docker_container = docker_containers['alice']
110+
alice_container = client.containers.get(alice_docker_container['Id'])
111+
112+
print(">>> shut down alice ...")
113+
alice_container.stop()
114+
115+
print(">>> waiting for alice container to exit ...")
116+
alice_id = alice_container.attrs['Id']
117+
wait_until_healthy(client, alice_id, is_healthy=False)
118+
alice_container.remove()
119+
120+
print(">>> start new alice container ...")
121+
new_alice_container = client.containers.run(
122+
'acapy-test',
123+
command=alice_container.attrs['Config']['Cmd'],
124+
detach=True,
125+
environment={'RUST_LOG': 'aries-askar::log::target=error'},
126+
healthcheck=alice_container.attrs['Config']['Healthcheck'],
127+
name='alice',
128+
network=alice_container.attrs['HostConfig']['NetworkMode'],
129+
ports=alice_container.attrs['NetworkSettings']['Ports'],
130+
)
131+
print(">>> new container:", 'alice', json.dumps(new_alice_container.attrs))
132+
alice_id = new_alice_container.attrs['Id']
133+
134+
try:
135+
wait_until_healthy(client, alice_id)
136+
print(">>> new alice container is healthy")
137+
138+
# run some more tests ... alice should still be connected to bob for example ...
139+
async with Controller(base_url=ALICE) as alice, Controller(base_url=BOB) as bob:
140+
# Present the the credential's attributes
141+
print(">>> present proof ... again ...")
142+
await indy_present_proof_v2(
143+
bob,
144+
alice,
145+
bob_conn.connection_id,
146+
alice_conn.connection_id,
147+
requested_attributes=[{"name": "firstname"}],
148+
)
149+
print(">>> Done! (again)")
150+
finally:
151+
# cleanup - shut down alice agent (not part of docker compose)
152+
print(">>> shut down alice ...")
153+
alice_container = client.containers.get(alice_id)
154+
alice_container.stop()
155+
wait_until_healthy(client, alice_id, is_healthy=False)
156+
alice_container.remove()
157+
158+
159+
if __name__ == "__main__":
160+
logging_to_stdout()
161+
asyncio.run(main())

0 commit comments

Comments
 (0)