Skip to content

Latest commit

 

History

History
219 lines (164 loc) · 6.33 KB

developing_backends.rst

File metadata and controls

219 lines (164 loc) · 6.33 KB

Developing Backends

Due to the unlimited amount of possible use cases and implementations available, we want to expose the ability for developers to create custom :ref:`backends`.

What Is A Backend?

A backend is a Python package that declares an entry_point in the jiav.backend group.

That entry point should reference a subclass of the jiav.backend.BaseBackend abstract base class. This allows jiav to discover installed backends and instantiate a selected backend at run-time.

Implementation

All backends are epxected to share the custom jiav.backend.Result interface, populate it and return it at the end:

Attribute Type Descrption
successful boolean Represents if the backend executed successfully.
errors List[str] List of strings containing errors.
output List[str] List of strings containing output.

All baceknds inherit from BaseBackend and must implement the following methods:

  • validate_schema - validate a schema using jsonschema.
  • execute_backend - Executes the backend, return a Result.

All backends are expected to subscribe to the global logger jiav.logger to log information.

Developing A Custom example Backend

Note

Based on a backend template.

In this section, we will create an example backend that checks if an environment variable is set.

  1. Your backend must register an entry point in the jiav.backend group, using the packaging software you use to build your project. With Poetry, you can define the entry point in your pyproject.toml file:

    [tool.poetry.plugins."jiav.backend"]
    example = "jiav_example.ExampleBackend"
    
  2. Ensure that your class is discovarable in the jiav_example namespace, populate src/jiav_example/__init__.py:

    from jiav_example.backend import ExampleBackend
    
    __all__ = ["ExampleBackend"]
  3. Create a src/jiav_example/backend.py and import all dependencies

    import os
    # Global logger
    from jiav import logger
    # Required interafces
    from jiav.backend import BaseBackend, Result
    # Type enforcement
    from typing import List
  4. Subscribe to the global logger:

    jiav_logger = logger.subscribe_to_logger()
  5. In the current implementation of backends, we need to create a mock step that will be used during JSON schema validation when constructing an initial object:

    # Mock step that will be used when initializing an initial object
    MOCK_STEP = {"example": "example"}
  6. define the schema that will be used to validate the step supplied by the user:

    SCHEMA = {
        "type": "object",
        "required": ["example"],
        "properties": {"example": {"type": "string"}},
        "additionalProperties": False,
    }
  7. Create an initial ExampleBackend interface which inherits from BaseBackend:

    class ExampleBackend(BaseBackend):
        """
        ExampleBackend object
    
        An example backend for jiav
    
        Attributes:
            name   - Backend name
            schema - json_schema to be used to verify that the supplied step is
                     valid according to the backends's requirements
            step   - Backend excution instructions
        """
    
        MOCK_STEP = {"example": "example"}
        SCHEMA = {
            "type": "object",
            "required": ["example"],
            "properties": {"example": {"type": "string"}},
            "additionalProperties": False,
        }
    
        def __init__(self) -> None:
            self.name = "example"
            self.schema = self.SCHEMA
            self.step = self.MOCK_STEP
            super().__init__(name=self.name, schema=self.schema, step=self.step)
  8. Implement execute_backend method that will execute the backend and return a Result:

    # Overrdie method of BaseBackend
    def execute_backend(self) -> None:
        """
        Execute backend
    
        Returns a namedtuple describing the jiav manifest execution
        """
        # Parse required arugments
        example: str = self.step["example"]
        output: List = []
        errors: List = []
        successful: bool = False
        jiav_logger.debug(f"Example: {example}")
        try:
            os.environ["JIAV_EXAMPLE"] = example
            successful = True
            jiav_logger.debug(
                f"Environment variable 'JIAV_EXAMPLE' was set to '{example}'"
            )
            output.append(f"Environment variable 'JIAV_EXAMPLE' was set to '{example}'")
        except Exception as e:
            jiav_logger.error(e.text)
            errors.append(e.text)
        self.result = Result(successful, output, errors)
  9. Install the package and verify example backend is registered in jiav:

    jiav --version
    jiav, version 0.3.0
    
    Installed Backends:
      - example, version {'version': '0.1.0', 'class': 'jiav_example.ExampleBackend'}
      - jira_issue, version {'version': '0.3.0', 'class': 'jiav_jira_issue.JiraIssueBackend'}
      - lineinfile, version {'version': '0.3.0', 'class': 'jiav_lineinfile.LineInFileBackend'}
      - regexinfile, version {'version': '0.3.0', 'class': 'jiav_regexinfile.RegexInFileBackend'}
  10. Create a test manifest /tmp/example_manifest.yaml, and verify that it is valid

    cat << EOF > /tmp/example_manifest.yaml
    jiav:
      verified_status: "Done"
      verification_steps:
        - name: "Example"
          backend: example
          example: "example"
    EOF
    export JIAV_EXAMPLE="/tmp/example_manifest.yaml"
    jiav validate-manifest --from-file="/tmp/example_manifest.yaml"