-
Notifications
You must be signed in to change notification settings - Fork 178
Implement plugins #2581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Implement plugins #2581
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
26a5c5d
Implement plugins prototype
r4victor 25d5dc8
Add per-configuration ApplyPolicy methods
r4victor cf0858b
Add get_plugin_logger()
r4victor 53b7150
feat: Add tests for loading dstack plugins
r4victor 00d9cea
Document get_plugin_logger()
r4victor b226519
Backport entry_points for Python 3.9
r4victor a109448
Add plugin example
r4victor ac90f0e
Document Concepts->Plugins
r4victor 690b585
Add example on setting service-specific properties in plugins
r4victor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# Plugins | ||
|
||
The `dstack` plugin system allows extending `dstack` server functionality using external Python packages. | ||
|
||
!!! info "Experimental" | ||
Plugins are currently an _experimental_ feature. | ||
Backward compatibility is not guaranteed across releases. | ||
|
||
## Enable plugins | ||
|
||
To enable a plugin, list it under `plugins` in [`server/config.yml`](../reference/server/config.yml.md): | ||
|
||
<div editor-title="server/config.yml"> | ||
|
||
```yaml | ||
plugins: | ||
- my_dstack_plugin | ||
- some_other_plugin | ||
projects: | ||
- name: main | ||
``` | ||
|
||
</div> | ||
|
||
On the next server restart, you should see a log message indicating that the plugin is loaded. | ||
|
||
## Create plugins | ||
|
||
To create a plugin, create a Python package that implements a subclass of | ||
`dstack.plugins.Plugin` and exports this subclass as a "dstack.plugins" entry point. | ||
|
||
1. Init the plugin package: | ||
|
||
<div class="termy"> | ||
|
||
```shell | ||
$ uv init --library | ||
``` | ||
|
||
</div> | ||
|
||
2. Define `ApplyPolicy` and `Plugin` subclasses: | ||
|
||
<div editor-title="src/example_plugin/__init__.py"> | ||
|
||
```python | ||
from dstack.plugins import ApplyPolicy, Plugin, RunSpec, get_plugin_logger | ||
|
||
logger = get_plugin_logger(__name__) | ||
|
||
class ExamplePolicy(ApplyPolicy): | ||
def on_run_apply(self, user: str, project: str, spec: RunSpec) -> RunSpec: | ||
# ... | ||
return spec | ||
|
||
class ExamplePlugin(Plugin): | ||
|
||
def get_apply_policies(self) -> list[ApplyPolicy]: | ||
return [ExamplePolicy()] | ||
``` | ||
|
||
</div> | ||
|
||
3. Specify a "dstack.plugins" entry point in `pyproject.toml`: | ||
|
||
<div editor-title="pyproject.toml"> | ||
|
||
```toml | ||
[project.entry-points."dstack.plugins"] | ||
example_plugin = "example_plugin:ExamplePlugin" | ||
``` | ||
|
||
</div> | ||
|
||
Then you can install the plugin package into your Python environment and enable it via `server/config.yml`. | ||
|
||
??? info "Plugins in Docker" | ||
If you deploy `dstack` using a Docker image you can add plugins either | ||
by including them in your custom image built upon the `dstack` server image, | ||
or by mounting installed plugins as volumes. | ||
|
||
## Apply policies | ||
|
||
Currently the only plugin functionality is apply policies. | ||
Apply policies allow modifying specs of runs, fleets, volumes, and gateways submitted on `dstack apply`. | ||
Subclass `dstack.plugins.ApplyPolicy` to implement them. | ||
|
||
Here's an example of how to enforce certain rules using apply policies: | ||
|
||
<div editor-title="src/example_plugin/__init__.py"> | ||
|
||
```python | ||
class ExamplePolicy(ApplyPolicy): | ||
def on_run_apply(self, user: str, project: str, spec: RunSpec) -> RunSpec: | ||
# Forcing some limits | ||
spec.configuration.max_price = 2.0 | ||
spec.configuration.max_duration = "1d" | ||
# Setting some extra tags | ||
if spec.configuration.tags is None: | ||
spec.configuration.tags = {} | ||
spec.configuration.tags |= { | ||
"team": "my_team", | ||
} | ||
# Forbid something | ||
if spec.configuration.privileged: | ||
logger.warning("User %s tries to run privileged containers", user) | ||
raise ValueError("Running privileged containers is forbidden") | ||
# Set some service-specific properties | ||
if isinstance(spec.configuration, Service): | ||
spec.configuration.https = True | ||
return spec | ||
``` | ||
|
||
</div> | ||
|
||
For more information on the plugin development, see the [plugin example](https://github.com/dstackai/dstack/tree/master/examples/plugins/example_plugin). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.11 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
## Overview | ||
|
||
This is a basic `dstack` plugin example. | ||
You can use it as a reference point when implementing new `dstack` plugins. | ||
|
||
## Steps | ||
|
||
1. Init the plugin package: | ||
|
||
``` | ||
uv init --library | ||
``` | ||
|
||
2. Define `ApplyPolicy` and `Plugin` subclasses: | ||
|
||
```python | ||
from dstack.plugins import ApplyPolicy, Plugin, RunSpec, get_plugin_logger | ||
|
||
|
||
logger = get_plugin_logger(__name__) | ||
|
||
|
||
class ExamplePolicy(ApplyPolicy): | ||
|
||
def on_run_apply(self, user: str, project: str, spec: RunSpec) -> RunSpec: | ||
# ... | ||
return spec | ||
|
||
|
||
class ExamplePlugin(Plugin): | ||
|
||
def get_apply_policies(self) -> list[ApplyPolicy]: | ||
return [ExamplePolicy()] | ||
|
||
``` | ||
|
||
3. Specify a "dstack.plugins" entry point in `pyproject.toml`: | ||
|
||
```toml | ||
[project.entry-points."dstack.plugins"] | ||
example_plugin = "example_plugin:ExamplePlugin" | ||
``` | ||
|
||
4. Make sure to install the plugin and enable it in the `server/config.yml`: | ||
|
||
```yaml | ||
plugins: | ||
- example_plugin | ||
projects: | ||
- name: main | ||
# ... | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[project] | ||
name = "example-plugin" | ||
version = "0.1.0" | ||
description = "A dstack plugin example" | ||
readme = "README.md" | ||
authors = [ | ||
{ name = "Victor Skvortsov", email = "[email protected]" } | ||
] | ||
requires-python = ">=3.9" | ||
dependencies = [] | ||
|
||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project.entry-points."dstack.plugins"] | ||
example_plugin = "example_plugin:ExamplePlugin" |
34 changes: 34 additions & 0 deletions
34
examples/plugins/example_plugin/src/example_plugin/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from dstack.api import Service | ||
from dstack.plugins import ApplyPolicy, GatewaySpec, Plugin, RunSpec, get_plugin_logger | ||
|
||
logger = get_plugin_logger(__name__) | ||
|
||
|
||
class ExamplePolicy(ApplyPolicy): | ||
def on_run_apply(self, user: str, project: str, spec: RunSpec) -> RunSpec: | ||
# Forcing some limits | ||
spec.configuration.max_price = 2.0 | ||
spec.configuration.max_duration = "1d" | ||
# Setting some extra tags | ||
if spec.configuration.tags is None: | ||
spec.configuration.tags = {} | ||
spec.configuration.tags |= { | ||
"team": "my_team", | ||
} | ||
# Forbid something | ||
if spec.configuration.privileged: | ||
logger.warning("User %s tries to run privileged containers", user) | ||
raise ValueError("Running privileged containers is forbidden") | ||
# Set some service-specific properties | ||
if isinstance(spec.configuration, Service): | ||
spec.configuration.https = True | ||
return spec | ||
|
||
def on_gateway_apply(self, user: str, project: str, spec: GatewaySpec) -> GatewaySpec: | ||
# Forbid creating new gateways altogether | ||
raise ValueError("Creating gateways is forbidden") | ||
|
||
|
||
class ExamplePlugin(Plugin): | ||
def get_apply_policies(self) -> list[ApplyPolicy]: | ||
return [ExamplePolicy()] |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.