Skip to content

Commit

Permalink
Merge remote-tracking branch 'hyperledger/master' into feature/mediat…
Browse files Browse the repository at this point in the history
…e_forward
  • Loading branch information
TelegramSam committed Aug 22, 2020
2 parents 7ca8e15 + a2530dd commit fe386d3
Show file tree
Hide file tree
Showing 178 changed files with 4,801 additions and 1,118 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# 0.5.3

## July 23, 2020

- Store endpoint on provisioned DID records [#610](https://github.com/hyperledger/aries-cloudagent-python/pull/610)
- More reliable delivery of outbound messages and webhooks [#615](https://github.com/hyperledger/aries-cloudagent-python/pull/615)
- Improvements for OpenShift pod handling [#614](https://github.com/hyperledger/aries-cloudagent-python/pull/614)
- Remove support for 'on-demand' revocation registries [#605](https://github.com/hyperledger/aries-cloudagent-python/pull/605)
- Sort tags in generated swagger JSON for better consistency [#602](https://github.com/hyperledger/aries-cloudagent-python/pull/602)
- Improve support for multi-credential proofs [#601](https://github.com/hyperledger/aries-cloudagent-python/pull/601)
- Adjust default settings for tracing and add documentation [#598](https://github.com/hyperledger/aries-cloudagent-python/pull/598), [#597](https://github.com/hyperledger/aries-cloudagent-python/pull/597)
- Fix reliance on local copy of revocation tails file [#590](https://github.com/hyperledger/aries-cloudagent-python/pull/590)
- Improved handling of problem reports [#595](https://github.com/hyperledger/aries-cloudagent-python/pull/595)
- Remove credential preview parameter from credential issue endpoint [#596](https://github.com/hyperledger/aries-cloudagent-python/pull/596)
- Looser format restrictions on dates [#586](https://github.com/hyperledger/aries-cloudagent-python/pull/586)
- Support `names` and attribute-value specifications in present-proof protocol [#587](https://github.com/hyperledger/aries-cloudagent-python/pull/587)
- Misc documentation updates and unit test coverage

# 0.5.2

## June 26, 2020
Expand Down
40 changes: 36 additions & 4 deletions aries_cloudagent/admin/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from ..config.injection_context import InjectionContext
from ..core.plugin_registry import PluginRegistry
from ..ledger.error import LedgerConfigError, LedgerTransactionError
from ..messaging.responder import BaseResponder
from ..transport.queue.basic import BasicMessageQueue
from ..transport.outbound.message import OutboundMessage
Expand Down Expand Up @@ -51,7 +52,7 @@ class AdminStatusLivelinessSchema(Schema):


class AdminStatusReadinessSchema(Schema):
"""Schema for the liveliness endpoint."""
"""Schema for the readiness endpoint."""

ready = fields.Boolean(description="Readiness status", example=True)

Expand Down Expand Up @@ -131,7 +132,18 @@ async def ready_middleware(request: web.BaseRequest, handler: Coroutine):
"/status/live",
"/status/ready",
) or request.app._state.get("ready"):
return await handler(request)
try:
return await handler(request)
except (LedgerConfigError, LedgerTransactionError) as e:
# fatal, signal server shutdown
LOGGER.error("Shutdown with %s", str(e))
request.app._state["ready"] = False
request.app._state["alive"] = False
raise
except Exception as e:
# some other error?
LOGGER.error("Handler error with exception: %s", str(e))
raise e

raise web.HTTPServiceUnavailable(reason="Shutdown in progress")

Expand Down Expand Up @@ -295,6 +307,11 @@ async def collect_stats(request, handler):
app=app, title=agent_label, version=version_string, swagger_path="/api/doc"
)
app.on_startup.append(self.on_startup)

# ensure we always have status values
app._state["ready"] = False
app._state["alive"] = False

return app

async def start(self) -> None:
Expand Down Expand Up @@ -329,6 +346,7 @@ async def start(self) -> None:
try:
await self.site.start()
self.app._state["ready"] = True
self.app._state["alive"] = True
except OSError:
raise AdminSetupError(
"Unable to start webserver with host "
Expand Down Expand Up @@ -429,7 +447,11 @@ async def liveliness_handler(self, request: web.BaseRequest):
The web response, always indicating True
"""
return web.json_response({"alive": True})
app_live = self.app._state["alive"]
if app_live:
return web.json_response({"alive": app_live})
else:
raise web.HTTPServiceUnavailable(reason="Service not available")

@docs(tags=["server"], summary="Readiness check")
@response_schema(AdminStatusReadinessSchema(), 200)
Expand All @@ -444,7 +466,11 @@ async def readiness_handler(self, request: web.BaseRequest):
The web response, indicating readiness for further calls
"""
return web.json_response({"ready": self.app._state["ready"]})
app_ready = self.app._state["ready"] and self.app._state["alive"]
if app_ready:
return web.json_response({"ready": app_ready})
else:
raise web.HTTPServiceUnavailable(reason="Service not ready")

@docs(tags=["server"], summary="Shut down server")
async def shutdown_handler(self, request: web.BaseRequest):
Expand All @@ -464,6 +490,12 @@ async def shutdown_handler(self, request: web.BaseRequest):

return web.json_response({})

def notify_fatal_error(self):
"""Set our readiness flags to force a restart (openshift)."""
LOGGER.error("Received shutdown request notify_fatal_error()")
self.app._state["ready"] = False
self.app._state["alive"] = False

async def websocket_handler(self, request):
"""Send notifications to admin client over websocket."""

Expand Down
42 changes: 42 additions & 0 deletions aries_cloudagent/admin/tests/test_admin_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ async def test_import_routes(self):
server = self.get_admin_server({"admin.admin_insecure_mode": True}, context)
app = await server.make_application()

async def test_register_external_plugin_x(self):
context = InjectionContext()
context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry())
with self.assertRaises(ValueError):
builder = DefaultContextBuilder(
settings={"external_plugins": "aries_cloudagent.nosuchmodule"}
)
await builder.load_plugins(context)

async def test_visit_insecure_mode(self):
settings = {"admin.admin_insecure_mode": True, "task_queue": True}
server = self.get_admin_server(settings)
Expand Down Expand Up @@ -257,3 +266,36 @@ async def test_visit_shutting_down(self):
) as response:
assert response.status == 200
await server.stop()

async def test_server_health_state(self):
settings = {
"admin.admin_insecure_mode": True,
}
server = self.get_admin_server(settings)
await server.start()

async with self.client_session.get(
f"http://127.0.0.1:{self.port}/status/live", headers={}
) as response:
assert response.status == 200
response_json = await response.json()
assert response_json["alive"]

async with self.client_session.get(
f"http://127.0.0.1:{self.port}/status/ready", headers={}
) as response:
assert response.status == 200
response_json = await response.json()
assert response_json["ready"]

server.notify_fatal_error()
async with self.client_session.get(
f"http://127.0.0.1:{self.port}/status/live", headers={}
) as response:
assert response.status == 503

async with self.client_session.get(
f"http://127.0.0.1:{self.port}/status/ready", headers={}
) as response:
assert response.status == 503
await server.stop()
3 changes: 1 addition & 2 deletions aries_cloudagent/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ def load_command(command: str):
for cmd in available_commands():
if cmd["name"] == command:
module = cmd["name"]
if "module" in cmd:
module_path = cmd["module"]
module_path = cmd.get("module")
break
if module and not module_path:
module_path = f"{__package__}.{module}"
Expand Down
9 changes: 7 additions & 2 deletions aries_cloudagent/commands/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ def execute(argv: Sequence[str] = None):
parser.print_help()


if __name__ == "__main__":
execute()
def main():
"""Execute the main line."""
if __name__ == "__main__":
execute()


main()
9 changes: 7 additions & 2 deletions aries_cloudagent/commands/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ def execute(argv: Sequence[str] = None):
loop.run_until_complete(provision(settings))


if __name__ == "__main__":
execute()
def main():
"""Execute the main line."""
if __name__ == "__main__":
execute()


main()
18 changes: 16 additions & 2 deletions aries_cloudagent/commands/tests/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ class TestHelp(AsyncTestCase):
def test_exec_help(self):
with async_mock.patch.object(
command.ArgumentParser, "print_help"
) as print_help:
) as mock_print_help, async_mock.patch(
"builtins.print", async_mock.MagicMock()
) as mock_print:
command.execute([])
print_help.assert_called_once()
mock_print_help.assert_called_once()

command.execute(["-v"])
mock_print.assert_called_once_with(command.__version__)

def test_main(self):
with async_mock.patch.object(
command, "__name__", "__main__"
) as mock_name, async_mock.patch.object(
command, "execute", async_mock.MagicMock()
) as mock_execute:
command.main()
mock_execute.assert_called_once
22 changes: 22 additions & 0 deletions aries_cloudagent/commands/tests/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from asynctest import TestCase as AsyncTestCase
from asynctest import mock as async_mock

from ... import commands as test_module


class TestInit(AsyncTestCase):
def test_available(self):
avail = test_module.available_commands()
assert len(avail) == 3

def test_run(self):
with async_mock.patch.object(
test_module, "load_command", async_mock.MagicMock()
) as mock_load:
mock_module = async_mock.MagicMock()
mock_module.execute = async_mock.MagicMock()
mock_load.return_value = mock_module

test_module.run_command("hello", ["world"])
mock_load.assert_called_once()
mock_module.execute.assert_called_once()
17 changes: 17 additions & 0 deletions aries_cloudagent/commands/tests/test_provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,20 @@ def test_bad_calls(self):
def test_provision_wallet(self):
test_seed = "testseed000000000000000000000001"
command.execute(["--wallet-type", "indy", "--seed", test_seed])

async def test_provision_ledger_configured(self):
with async_mock.patch.object(
command, "wallet_config", async_mock.CoroutineMock()
) as mock_wallet_config, async_mock.patch.object(
command, "ledger_config", async_mock.CoroutineMock(return_value=True)
) as mock_ledger_config:
await command.provision({})

def test_main(self):
with async_mock.patch.object(
command, "__name__", "__main__"
) as mock_name, async_mock.patch.object(
command, "execute", async_mock.MagicMock()
) as mock_execute:
command.main()
mock_execute.assert_called_once
18 changes: 17 additions & 1 deletion aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,25 +394,41 @@ def add_arguments(self, parser: ArgumentParser):
The endpoints are used in the formation of a connection\
with another agent.",
)
parser.add_argument(
"--profile-endpoint",
type=str,
metavar="<profile_endpoint>",
help="Specifies the profile endpoint for the (public) DID.",
)
parser.add_argument(
"--read-only-ledger",
action="store_true",
help="Sets ledger to read-only to prevent updates.\
Default: false.",
)
parser.add_argument(
"--tails-server-base-url",
type=str,
metavar="<tails-server-base-url>",
help="Sets the base url of the tails server in use.",
)

def get_settings(self, args: Namespace) -> dict:
"""Extract general settings."""
settings = {}
if args.external_plugins:
settings["external_plugins"] = args.external_plugins
if args.storage_type:
settings["storage.type"] = args.storage_type
settings["storage_type"] = args.storage_type
if args.endpoint:
settings["default_endpoint"] = args.endpoint[0]
settings["additional_endpoints"] = args.endpoint[1:]
if args.profile_endpoint:
settings["profile_endpoint"] = args.profile_endpoint
if args.read_only_ledger:
settings["read_only_ledger"] = True
if args.tails_server_base_url:
settings["tails_server_base_url"] = args.tails_server_base_url
return settings


Expand Down
6 changes: 3 additions & 3 deletions aries_cloudagent/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ def __iter__(self):
def __getitem__(self, index):
"""Fetch as an array index."""
if not isinstance(index, str):
raise TypeError("Index must be a string")
raise TypeError(f"Index {index} must be a string")
missing = object()
result = self.get_value(index, default=missing)
if result is missing:
raise KeyError("Undefined index: {}".format(index))
return self.get_value(index)
return result

@abstractmethod
def __len__(self):
Expand Down Expand Up @@ -111,7 +111,7 @@ async def inject(
base_cls: type,
settings: Mapping[str, object] = None,
*,
required: bool = True
required: bool = True,
) -> object:
"""
Get the provided instance of a given class identifier.
Expand Down
5 changes: 5 additions & 0 deletions aries_cloudagent/config/default_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ..issuer.base import BaseIssuer
from ..holder.base import BaseHolder
from ..verifier.base import BaseVerifier
from ..tails.base import BaseTailsServer

from ..protocols.actionmenu.v1_0.base_service import BaseMenuService
from ..protocols.actionmenu.v1_0.driver_service import DriverMenuService
Expand Down Expand Up @@ -119,6 +120,10 @@ async def bind_providers(self, context: InjectionContext):
ClassProvider.Inject(BaseLedger),
),
)
context.injector.bind_provider(
BaseTailsServer,
ClassProvider("aries_cloudagent.tails.indy_tails_server.IndyTailsServer",),
)

# Register default pack format
context.injector.bind_provider(
Expand Down
2 changes: 1 addition & 1 deletion aries_cloudagent/config/injection_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(
):
"""Initialize a `ServiceConfig`."""
self._injector = Injector(settings, enforce_typing=enforce_typing)
self._scope_name = self.ROOT_SCOPE
self._scope_name = InjectionContext.ROOT_SCOPE
self._scopes = []

@property
Expand Down

0 comments on commit fe386d3

Please sign in to comment.