Last updated Jan 2, 2025

Replacing Sources/Modifiers with Context

On This Page

    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: