owned this note
owned this note
Published
Linked with GitHub
# Plugin file for ACA-Py
Based on https://github.com/hyperledger/aries-cloudagent-python/issues/1121.
## Config File improvements
The `plugins.yaml` structure could be the following:
```yaml=
plugins:
- plugin_name: Universal Resolver
local_directory: uni-plugin
# Optional configuration by key-value
config:
key1: value1
key2: value2
- plugin_name: DIDComm Resolver
local_directory: didcom-plugin
# Optional configuration by file
config: "config/config-file.yaml"
```
## Logic Sequence
In this section is going to be described the entire flow from running aca-py with a external plugin like the following:
```
aca-py start --arg-file default.yml --plugin mock_resolver
```
### 1. Arguments flow
This logic is placed in `aries_cloudagent/config/argparse.py`
[Argument caption](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/argparse.py#L467)
```python=
parser.add_argument(
"--plugin",
dest="external_plugins",
type=str,
action="append",
required=False,
metavar="<module>",
env_var="ACAPY_PLUGIN",
help=(
"Load <module> as external plugin module. Multiple "
"instances of this parameter can be specified."
),
)
```
[set argument into settings](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/argparse.py#L546)
```python=
if args.external_plugins:
settings["external_plugins"] = args.external_plugins
```
### 2. Register and load pluggins
This logic is placed in `aries_cloudagent/config/default_context.py`
[Register plugins](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/default_context.py#L131)
```python=
# Register external plugins
for plugin_path in self.settings.get("external_plugins", []):
plugin_registry.register_plugin(plugin_path)
```
[init the plugin](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/default_context.py#L136)
```python=
await plugin_registry.init_context(context)
```
The registration and the init is managed by the class `PluginRegistry`, `aries_cloudagent/core/plugin_registry.py`
#### 2.1 Register plugins
[Registration logic](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/core/plugin_registry.py#L116): The module is loaded on a class private variable.
```python=
self._plugins[module_name] = mod
```
#### 2.2 Init plugins
[Init the plugins](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/core/plugin_registry.py#L196): The module previously loaded launches the plugin setup.
```python=
async def init_context(self, context: InjectionContext):
"""Call plugin setup methods on the current context."""
for plugin in self._plugins.values():
if hasattr(plugin, "setup"):
await plugin.setup(context)
else:
await self.load_protocols(context, plugin)
```
## How to pass the config (OPTIONS to choose the best)
### ACA-Py flag
inside of [config/argparse.py](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/argparse.py) ACA-Py uses [configargparse](https://pypi.org/project/ConfigArgParse/) which supports yaml file parsing. we can update [external_plugins](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/argparse.py#L547) flag to parse and load a config.yaml file into `settings["external_plugins"]` this would expose all loaded configurations inside the context to be used inside each module.([example](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/multitenant/manager.py#L196))
We could also update registering to have more setup from configs, possibly [here](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/default_context.py#L131)
### Through Environment Variables
- pros
- ...
- cons
- String value. Encode and decode required
#### example
```python=
import os
import json
# Set environment variables
os.environ['config'] = json.dumps({'key': "value"})
# Get environment variables
config = json.loads(os.environ.get('config'))
```
### Through the context
- pros
- ...
- cons
- Change the base classes like `InjectionContext`
### Overload the setup plugin method
- pros
- ...
- cons
- The validation of the config relies on the plugin `setup` method
## Posible flow to adapt ACA-py
Following the current flow described on [Logic Sequence](https://hackmd.io/ROUzENdpQ12cz3UB9qk1nA#Logic-Sequence)
:::warning
This is a implementation example :zap:
Based on: Overload the setup plugin method
:::
Having the folowing file `plugin.yaml`
```yaml=
plugins:
- plugin_name: Universal Resolver
local_directory: http_uniresolver
config:
methods:
- "sov"
- "btcr"
```
It is posible to deploy ACA-Py with the plugin configuration.
```
aca-py start --arg-file default.yml --plugin-config plugin.yaml
```
### 1. Arguments flow
Add the following in `aries_cloudagent/config/argparse.py`
[Argument caption](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/argparse.py#L467)
> Add below the current implementation
```python=
parser.add_argument(
"--plugin-config",
dest="plugin_config",
type=str,
action="append",
required=False,
metavar="<module>",
env_var="ACAPY_PLUGIN_CONFIG",
help=(
"Load <module> as external plugin module config. Multiple "
"instances of this parameter can be specified."
),
)
```
[set argument into settings](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/argparse.py#L546)
> Add below the current implementation
```python=
if args.external_plugins:
with open(args.external_plugins, 'r') as stream:
plugins_conf = yaml.safe_load(stream)
if not settings["external_plugins"]:
settings["external_plugins"] = []
settings["plugins_config"] = {}
for plugin in plugins_conf.get("plugins"):
plug_dir = plugin.get("local_directory")
plug_conf = plugin.get("config")
settings["external_plugins"].append(plug_dir)
settings["plugins_config"][plug_dir] = plug_conf
```
### 2. Register and load pluggins
This logic is placed in `aries_cloudagent/config/default_context.py`
[init the plugin](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/config/default_context.py#L136)
> Add an argument with the plugins configuration
```python=
await plugin_registry.init_context(context, self.settings.get("plugins_config"))
```
The init is managed by the class `PluginRegistry`, `aries_cloudagent/core/plugin_registry.py`
#### 2.2 Init plugins
[Init the plugins](https://github.com/hyperledger/aries-cloudagent-python/blob/1c7bc86b91cd10d472ad15cb52c09b9424e8175c/aries_cloudagent/core/plugin_registry.py#L196): The module previously loaded launches the plugin setup.
> Add the argument in the method and the implementation
```python=
async def init_context(self, context: InjectionContext, plugins_config: dict = None):
"""Call plugin setup methods on the current context."""
for key, plugin in self._plugins.items():
if hasattr(plugin, "setup"):
plugin_conf = plugins_config.get(key,{})
await plugin.setup(context, **plugin_conf)
else:
await self.load_protocols(context, plugin)
```
## Possible improvements
This configuration could be extended adding more plugin entry points, for instance could be possible replace the local_directory to:
```yaml=
git-repo: "https://github.com/sicpa-dlab/acapy-resolver-universal"
```
or
```yaml=
pypi-package: plugin-test
```