Last updated Jan 2, 2025

Replacing Sources/Modifiers with Context

Background

When Sovereign was first built, we started with the concept of "Sources", acting as the main data source that was polled and brought into the templates to generate dynamic config.

Later, in order to transform that data into a format that was more suitable for our templates internally (in Atlassian), we create the Modifier system, which allowed us to add our own extensions without affecting the open source codebase.

Sources contained data that was polled more frequently and filtered based on the envoy discovery request, and then Template Context was added in after this, to represent pieces of data that were relativelt static and unfiltered.

Problems

After years of operating Sovereign we have learnt that we can remove Sources/Modifiers completely in favor of Template Context, and reimplement the matching/filtering that was previously in Sovereign (which makes caching responses difficult and also forces sovereign to do a lot of computational work which is not great in Python).

Migrating

Simple use-cases

Let's say you had a https source like the following:

1
2
sources: 
  - type: file
    config:
      spec:
        protocol: https
        serialization: json
        path: somewhere.com/api/instances.json

You would replace this with a single template context:

1
2
template_context:
  context:
    instances:
      protocol: https
      serialization: json
      path: https://somewhere.com/api/instances.json

Advanced use-cases

To replicate your source as a context plugin we highly recommend reading how to create your own context plugin

Example source (deprecated):

1
2
from typing import Any, Dict
from sovereign.sources.lib import Source
from sovereign.dynamic_config import Loadable


class MySource(Source):
    def __init__(self, config: Dict[str, Any], scope: str = "default"):
        super(MySource, self).__init__(config, scope)
        try:
            self.path = Loadable.from_legacy_fmt(config["path"])
        except KeyError:
            try:
                self.path = Loadable(**config["spec"])
            except KeyError:
                raise KeyError('File source needs to specify "spec" within config')

    def get(self) -> Any:
        return self.path.load()

Example of changes required:

1
2
from typing import Any
from sovereign.dynamic_config import Loadable
from sovereign.dynamic_config.loaders import CustomLoader


class MyPlugin(CustomLoader):
    default_deser = "none"
    def load(self, path: str) -> Any:
        loadable = Loadable.from_legacy_fmt(path)
        return loadable.load()

Rate this page: