- How-to Guides
- Nebari Extension System
Nebari Extension System
Introduction
Section titled “Introduction”This guide is to help developers extend and modify the behavior of Nebari. We leverage the plugin system pluggy to enable easy extensibility. Currently, Nebari supports:
- overriding a given stages in a deployment
- adding additional stages before, after and between existing stages
- arbitrary subcommands
We maintain an examples repository which contains up to date usable examples.
Installing a plugin
Section titled “Installing a plugin”Registering a plugin should be easy for the end user. Plugins are either installed into your existing environment e.g.
pip install my-nebari-pluginAlternatively if you only want to temporarily add an extension.
nebari --import-plugin path/to/plugin.py <command> ...nebari --import-plugin import.module.plugin.path <command> ...Developing an extension
Section titled “Developing an extension”The most important step to developing a plugin is ensuring that the setuptools entrypoint is set.
...
[project.entry-points.nebari]my-subcommand = "path.to.subcommand.module"
...Adding this one line to your pyproject.toml will ensure that upon
installation of the package the Nebari plugins are registered.
Subcommands
Section titled “Subcommands”Nebari exposes a hook nebari_subcommand which exposes the
typer CLI instance. This allows
the developer to attach any arbitrary number of subcommands.
from nebari.hookspecs import hookimpl
import typer
@hookimpldef nebari_subcommand(cli): @cli.command() def hello( name: str = typer.Option( "Nebari", help="Who to say hello to" ) ): print(f"Hello {name}")There is a dedicated working example in nebari-plugin-examples.
Stages
Section titled “Stages”Nebari exposes a hook nebari_stage which uses the NebariStage
class. NebriStage exposes render, deploy, destroy as
arbitrary functions calls. See below for a complete example.
Nebari decides the order of stages based on two things the name: str
attribute and priority: int attribute. The rules are as follows:
- stages are ordered by
priority - stages which have the same
namethe one with highest priority number is chosen
import contextlibimport osfrom typing import Dict, Any
from nebari.hookspecs import hookimpl, NebariStage
class HelloWorldStage(NebariStage): name = "hello_world" priority = 100
def render(self): return { "hello_world.txt": "File that says hello world" }
@contextlib.contextmanager def deploy(self, stage_outputs: Dict[str, Dict[str, Any]]): print("I ran deploy") # set environment variables for stages that run after os.environ['HELLO'] = 'WORLD' # set output state for future stages to use stage_outputs[self.name] = {'hello': 'world'} yield # cleanup after deployment (rarely needed) os.environ.pop('HELLO')
def check(self, stage_outputs: Dict[str, Dict[str, Any]]): if 'HELLO' not in os.environ: raise ValueError('stage did not deploy successfully since HELLO environment variable not set')
@contextlib.contextmanager def destroy(self, stage_outputs: Dict[str, Dict[str, Any]], status: Dict[str, bool]): print('faking to destroy things for hello world stage') yield
@hookimpldef nebari_stage(): return [HelloWorldStage]There is a dedicated working example in nebari-plugin-examples.