As explained a little bit in Terminology, modifiers are a sort of plugin system.
Modifiers are Python modules that can be configured to run while Sovereign is in operation, to transform Instances in some way.
There are two types of modifiers:
In general, modifiers should be used when you need to change the data from your Sources in some way before it is used to generate Envoy configuration with Templates
Maybe your data requires additional validation, transformation, or some other complex operation that would be too messy in template code.
Or maybe the data from your Sources is user-friendly, but you need to convert this into something more structured.
Modifiers run in the same background thread that fetches Sources, which means that they do not impact the performance of discovery requests, and have no cost when it comes to generating envoy configuration from templates.
Modifiers are installed using entry points in Python.
The following steps give a contrived example of how you would create and install a modifier into Sovereign using entry points.
The following tutorial will guide you through the creation of a Modifier which adds a
key:value pair {"foo": "bar"}
to every instance that matches certain conditions.
For this short tutorial, let's imagine that we have configured the inline sources from the tutorial, so that we have some instances to work with.
First, create an empty Python module.
For this example the following folder structure will be used:
1 2├───my_custom_modifier │ ├───__init__.py # <- This file is left empty │ └───foobar.py ├───setup.py └───sovereign.yaml
You'll notice a sovereign.yaml
file in the above folder structure.
It's expected that modifiers are stored and installed in the same directory as your configuration.
However, this is not strictly required, and you can install a modifier from anywhere on the filesystem,
as long as it is installed with the same Python runtime which is going to run Sovereign.
Sovereign provides a Modifier class which has some methods that must be implemented.
1 2# my_custom_modifier/foobar.py from sovereign.modifiers.lib import Modifier class AddFooBar(Modifier): """ Adds a key:value "foo":"bar" to each instance """ def match(self) -> bool: """ In order for the Modifier to apply to an instance, this function must return True. """ return self.instance.get('group') == 'A' def apply(self) --> dict: """ If `match` returned True, this function is called. Perform modifications to self.instance and then return it """ # Add {"foo": "bar"} to the instance. self.instance['foo'] = 'bar' return self.instance
This modifier, when executed, should only apply to the first instance, since the match
condition is that the instance has a key group
with a value of A
.
The following script adds your Python module to the list of modifiers, which Sovereign checks at runtime:
1 2from setuptools import setup, find_packages setup( name='my_custom_modifier', packages=find_packages(), entry_points={ "sovereign.modifiers": [ "foobar_injector = my_custom_modifier.foobar:AddFooBar", ] } )
This will install the above Python module into an entry point named sovereign.modifiers
,
with a name of foobar_injector
You'll need to run the above setup script wherever you've installed Sovereign, using pip install sovereign
or similar.
Simply run python setup.py install
and you should see output similar to the following:
1 2$ python setup.py install running install running bdist_egg running egg_info writing my_custom_modifier.egg-info\PKG-INFO writing dependency_links to my_custom_modifier.egg-info\dependency_links.txt writing entry points to my_custom_modifier.egg-info\entry_points.txt writing top-level names to my_custom_modifier.egg-info\top_level.txt reading manifest file 'my_custom_modifier.egg-info\SOURCES.txt' writing manifest file 'my_custom_modifier.egg-info\SOURCES.txt' installing library code to build\bdist.win32\egg running install_lib running build_py creating build\bdist.win32\egg creating build\bdist.win32\egg\my_custom_modifier copying build\lib\my_custom_modifier\foobar.py -> build\bdist.win32\egg\my_custom_modifier copying build\lib\my_custom_modifier\__init__.py -> build\bdist.win32\egg\my_custom_modifier byte-compiling build\bdist.win32\egg\my_custom_modifier\foobar.py to foobar.cpython-38.pyc byte-compiling build\bdist.win32\egg\my_custom_modifier\__init__.py to __init__.cpython-38.pyc creating build\bdist.win32\egg\EGG-INFO copying my_custom_modifier.egg-info\PKG-INFO -> build\bdist.win32\egg\EGG-INFO copying my_custom_modifier.egg-info\SOURCES.txt -> build\bdist.win32\egg\EGG-INFO copying my_custom_modifier.egg-info\dependency_links.txt -> build\bdist.win32\egg\EGG-INFO copying my_custom_modifier.egg-info\entry_points.txt -> build\bdist.win32\egg\EGG-INFO copying my_custom_modifier.egg-info\top_level.txt -> build\bdist.win32\egg\EGG-INFO zip_safe flag not set; analyzing archive contents... creating 'dist\my_custom_modifier-0.0.0-py3.8.egg' and adding 'build\bdist.win32\egg' to it removing 'build\bdist.win32\egg' (and everything under it) Processing my_custom_modifier-0.0.0-py3.8.egg Copying my_custom_modifier-0.0.0-py3.8.egg to .....\lib\site-packages Adding my-custom-modifier 0.0.0 to easy-install.pth file Installed .....\lib\site-packages\my_custom_modifier-0.0.0-py3.8.egg Processing dependencies for my-custom-modifier==0.0.0 Finished processing dependencies for my-custom-modifier==0.0.0
Sovereign will execute any modifiers added to its main configuration
1 2# /etc/sovereign.yaml modifiers: - foobar_injector
The minimum working example (again, using the sources from the tutorial) would be the following:
1 2modifiers: - foobar_injector sources: - type: inline config: instances: - name: instance-A-01 address: 10.0.0.50 group: A id: '01' - type: inline config: instances: - name: instance-B-01 address: 10.100.24.10 group: B id: '01' templates: default: {}
Once Sovereign is running, you can see that it modified the first instance but left the second one unchanged.
There is a debugging endpoint available: /admin/source_dump
.
This accepts a query parameter, modified
(default is yes
) so that you can see the instances before and after being modified.
1 2$ curl localhost:8000/admin/source_dump?modified=yes | jq [ { "name": "instance-A-01", "address": "10.0.0.50", "group": "A", "id": "01", "foo": "bar" }, { "name": "instance-B-01", "address": "10.100.24.10", "group": "B", "id": "01" } ]
match
, and then an apply
which performs some action on matched instancesfoobar_injector
to the list of modifiers, therefore informing Sovereign to execute it/admin/source_dump
debugging endpointRate this page: