docs: first draft for doc website

This commit is contained in:
Alexis Couvreur
2023-05-10 17:48:39 -04:00
parent 2f68cb2b15
commit 1322a16320
37 changed files with 1296 additions and 32 deletions

0
docs/.nojekyll Normal file
View File

23
docs/README.md Normal file
View File

@@ -0,0 +1,23 @@
![Latest Build](https://img.shields.io/github/actions/workflow/status/acouvreur/sablier/build.yml?style=flat-square&branch=main)![Go Report](https://goreportcard.com/badge/github.com/acouvreur/sablier?style=flat-square) ![Go Version](https://img.shields.io/github/go-mod/go-version/acouvreur/sablier?style=flat-square) ![Latest Release](https://img.shields.io/github/release/acouvreur/sablier/all.svg?style=flat-square)
# Sablier - Scale to Zero
Sablier is an API that start containers for a given duration.
It provides an integrations with multiple reverse proxies and different loading strategies.
Which allows you to start your containers on demand and shut them down automatically as soon as there's no activity.
![Hourglass](https://raw.githubusercontent.com/acouvreur/sablier/main/docs/img/hourglass.png)
## Glossary
I'll use these terms in order to be provider agnostic.
- **Session**: A Session is a set of **instances**
- **Instance**: An instance is either a docker container, docker swarm service, kubernetes deployment or kubernetes statefulset
## Credits
- [Hourglass icons created by Vectors Market - Flaticon](https://www.flaticon.com/free-icons/hourglass)
- [tarampampam/error-pages](https://github.com/tarampampam/error-pages/) for the themes

24
docs/_sidebar.md Normal file
View File

@@ -0,0 +1,24 @@
- [Introduction](/)
- [Getting started](/getting-started)
- [Installation](/installation)
- [Configuration](/configuration)
- [Strategies](/strategies)
- [Themes](/themes)
- [FAQ](/faq)
- [Versioning](/versioning)
- **Providers**
- [Overview](/providers/overview)
- [Docker](/providers/docker)
- [Docker Swarm](/providers/docker_swarm)
- [Kubernetes](/providers/kubernetes)
- **Reverse Proxy Plugins**
- [Overview](/plugins/overview)
- [Traefik](/plugins/traefik)
- [Nginx](/plugins/nginx)
- [Caddy](/plugins/caddy)
- **Guides**
- [Overview](/guides/overview)
- [VSCode Server with Traefik and Kubernetes](/guides/code-server-traefik-kubernetes.md)
- **Links**
- [<img src="/assets/img/github.svg" />Github](https://github.com/acouvreur/sablier)
- [<img src="/assets/img/docker.svg" height=24px />Docker Hub](https://hub.docker.com/r/acouvreur/sablier)

95
docs/api/README.md Normal file
View File

@@ -0,0 +1,95 @@
# Documentation for Sablier
<a name="documentation-for-api-endpoints"></a>
## Documentation for API Endpoints
All URIs are relative to *http://localhost:10000*
| Class | Method | HTTP request | Description |
|------------ | ------------- | ------------- | -------------|
| *ScaleApi* | [**scaleBlocking**](Apis/ScaleApi.md#scaleblocking) | **GET** /api/strategies/blocking | Hangs the request until the services are ready |
*ScaleApi* | [**scaleDynamic**](Apis/ScaleApi.md#scaledynamic) | **GET** /api/strategies/dynamic | The waiting page for the given services |
| *ThemeApi* | [**getTheme**](Apis/ThemeApi.md#gettheme) | **GET** /api/strategies/dynamoc/themes | |
<a name="documentation-for-models"></a>
## Documentation for Models
- [instance](./Models/instance.md)
- [session](./Models/session.md)
- [status](./Models/status.md)
- [themes](./Models/themes.md)
<a name="documentation-for-authorization"></a>
## Documentation for Authorization
All endpoints do not require authorization.
## API
To run the following examples you can create two containers:
- `docker create --name nginx nginx`
- `docker create --name apache httpd`
### GET `/api/strategies/dynamic`
**Description**: The `/api/strategies/dynamic` endpoint allows you to request a waiting page for multiple instances
| Parameter | Value | Description |
| -------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `names` | array of string | The instances to be started (cannot be used with `group` parameter) |
| `group` | string | The instance group to be started (using `sablier.group=mygroup` labels) (cannot be used with `names` parameter) |
| `session_duration` | duration [time.ParseDuration](https://pkg.go.dev/time#ParseDuration) | The session duration for all services, which will reset at each subsequent calls |
| `show_details` *(optional)* | bool | The details about instances |
| `display_name` *(optional)* | string | The display name |
| `theme` *(optional)* | string | The theme to use |
| `refresh_frequency` *(optional)* | duration [time.ParseDuration](https://pkg.go.dev/time#ParseDuration) | The refresh frequency for the loading page |
Go to http://localhost:10000/api/strategies/dynamic?names=nginx&names=apache&session_duration=5m&show_details=true&display_name=example&theme=hacker-terminal&refresh_frequency=10s and you should see
A special header `X-Sablier-Session-Status` is returned and will have the value `ready` if all instances are ready. Or else `not-ready`.
![API Dynamic Prompt image](docs/img/api-dynamic.png)
### GET `/api/strategies/blocking`
**Description**: The `/api/strategies/blocking` endpoint allows you to wait until the instances are ready
| Parameter | Value | Description |
| ---------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `names` | array of string | The instances to be started (cannot be used with `group` parameter) |
| `group` | string | The instance group to be started (using `sablier.group=mygroup` labels) (cannot be used with `names` parameter) |
| `session_duration` | duration [time.ParseDuration](https://pkg.go.dev/time#ParseDuration) | The session duration for all services, which will reset at each subsequent calls |
| `timeout` *(optional)* | duration [time.ParseDuration](https://pkg.go.dev/time#ParseDuration) | The maximum time to wait for instances to be ready |
A special header `X-Sablier-Session-Status` is returned and will have the value `ready` if all instances are ready. Or else `not-ready`.
**Curl example**
```bash
curl -X GET -v "http://localhost:10000/api/strategies/blocking?names=nginx&names=apache&session_duration=5m&timeout=5s"
* Trying 127.0.0.1:10000...
* Connected to localhost (127.0.0.1) port 10000 (#0)
> GET /api/strategies/blocking?names=nginx&names=apache&session_duration=5m&timeout=30s HTTP/1.1
> Host: localhost:10000
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< X-Sablier-Session-Status: ready
< Date: Mon, 14 Nov 2022 19:20:50 GMT
< Content-Length: 245
<
{"session":
{"instances":
[
{"instance":{"name":"nginx","currentReplicas":1,"desiredReplicas":1,"status":"ready"},"error":null},
{"instance":{"name":"apache","currentReplicas":1,"desiredReplicas":1,"status":"ready"},"error":null}
],
"status":"ready"
}
}
```

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
aria-label="Docker" role="img"
viewBox="0 0 512 512"><path stroke="#066da5" stroke-width="38" d="M296 226h42m-92 0h42m-91 0h42m-91 0h41m-91 0h42m8-46h41m8 0h42m7 0h42m-42-46h42"/><path fill="#066da5" d="m472 228s-18-17-55-11c-4-29-35-46-35-46s-29 35-8 74c-6 3-16 7-31 7H68c-5 19-5 145 133 145 99 0 173-46 208-130 52 4 63-39 63-39"/></svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

122
docs/configuration.md Normal file
View File

@@ -0,0 +1,122 @@
# Configuration
There are three different ways to define configuration options in Sablier:
1. In a configuration file
2. As environment variables
3. In the command-line arguments
These ways are evaluated in the order listed above.
If no value was provided for a given option, a default value applies.
## Configuration File
At startup, Sablier searches for configuration in a file named sablier.yml (or sablier.yaml) in:
- `/etc/sablier/`
- `$XDG_CONFIG_HOME/`
- `$HOME/.config/`
- `.` *(the working directory).*
You can override this using the configFile argument.
```bash
sablier --configFile=path/to/myconfigfile.yml
```
```yaml
provider:
# Provider to use to manage containers (docker, swarm, kubernetes)
name: docker
server:
# The server port to use
port: 10000
# The base path for the API
base-path: /
storage:
# File path to save the state (default stateless)
file:
sessions:
# The default session duration (default 5m)
default-duration: 5m
# The expiration checking interval.
# Higher duration gives less stress on CPU.
# If you only use sessions of 1h, setting this to 5m is a good trade-off.
expiration-interval: 20s
logging:
level: trace
strategy:
dynamic:
# Custom themes folder, will load all .html files recursively (default empty)
custom-themes-path:
# Show instances details by default in waiting UI
show-details-by-default: false
# Default theme used for dynamic strategy (default "hacker-terminal")
default-theme: hacker-terminal
# Default refresh frequency in the HTML page for dynamic strategy
default-refresh-frequency: 5s
blocking:
# Default timeout used for blocking strategy (default 1m)
default-timeout: 1m
```
## Environment Variables
All environment variables can be used in the form of the config file such as
```yaml
strategy:
dynamic:
custom-themes-path: /my/path
```
Becomes
```bash
STRATEGY_DYNAMIC_CUSTOM_THEMES_PATH=/my/path
```
## Arguments
To get the list of all available arguments:
```bash
sablier --help
# or
docker run acouvreur/sablier[:version] --help
# ex: docker run acouvreur/sablier:1.4.0-beta.3 --help
```
All arguments can be used in the form of the config file such as
```yaml
strategy:
dynamic:
custom-themes-path: /my/path
```
Becomes
```bash
sablier start --strategy.dynamic.custom-themes-path /my/path
```
## Reference
```
-h, --help help for start
--provider.name string Provider to use to manage containers [docker swarm kubernetes] (default "docker")
--server.base-path string The base path for the API (default "/")
--server.port int The server port to use (default 10000)
--sessions.default-duration duration The default session duration (default 5m0s)
--sessions.expiration-interval duration The expiration checking interval. Higher duration gives less stress on CPU. If you only use sessions of 1h, setting this to 5m is a good trade-off. (default 20s)
--storage.file string File path to save the state
--strategy.blocking.default-timeout duration Default timeout used for blocking strategy (default 1m0s)
--strategy.dynamic.custom-themes-path string Custom themes folder, will load all .html files recursively
--strategy.dynamic.default-refresh-frequency duration Default refresh frequency in the HTML page for dynamic strategy (default 5s)
--strategy.dynamic.default-theme string Default theme used for dynamic strategy (default "hacker-terminal")
--strategy.dynamic.show-details-by-default Show the loading instances details by default (default true)
```

5
docs/faq.md Normal file
View File

@@ -0,0 +1,5 @@
# Frequenty Asked Questions
## How to start multiple instances?
## How do I do

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

178
docs/getting-started.md Normal file
View File

@@ -0,0 +1,178 @@
# Getting started
This Getting Started will get you through what you need to understand how to use Sablier as a scale to zero middleware with a reverse proxy.
![integration](assets/img/integration.png)
## Identify your provider
The first thing you need to do is to identify your [Provider](/providers/overview).
?> A Provider is how Sablier can interact with your instances and scale them up and down to zero.
You can check the available providers [here](/providers/overview?id=available-providers).
## Identify your reverse proxy
Once you've identified you're [Provider](/providers/overview), you'll want to identify your [Reverse Proxy](/plugins/overview).
?> Because Sablier is designed as an API that can be used on its own, reverse proxy integrations acts as a client of that API.
You can check the available reverse proxy plugins [here](/plugins/overview?id=available-reverse-proxies)
## Connect it all together
- Let's say we're using the [Docker Provider](/providers/docker).
- Let's say we're using the [Caddy Reverse Proxy Plugin](/plugins/caddy).
### 1. Initial setup with Caddy
Suppose this is your initial setup with Caddy. You have your reverse proxy with a Caddyfile that does a simple reverse proxy on `/whoami`.
<!-- tabs:start -->
#### **docker-compose.yaml**
```yaml
services:
proxy:
image: caddy:2.6.4
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
whoami:
image: containous/whoami:v1.5.0
```
#### **Caddyfile**
```Caddyfile
:80 {
route /whoami {
reverse_proxy whoami:80
}
}
```
<!-- tabs:end -->
At this point you can run `docker compose up` and go to `http://localhost:8080/whoami` and you will see your service.
### 2. Install Sablier with the Docker Provider
Add the Sablier container in the `docker-compose.yaml` file.
```yaml
services:
proxy:
image: caddy:2.6.4
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
whoami:
image: containous/whoami:v1.5.0
sablier:
image: acouvreur/sablier:1.4.0-beta.3
command:
- start
- --provider.name=docker
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
```
### 3. Add the Sablier Caddy Plugin to Caddy
Because Caddy does not provide any runtime evaluation for the plugins, we need to build Caddy with this specific plugin.
I'll use the provided Dockerfile to build the custom Caddy image.
```bash
docker build https://github.com/acouvreur/sablier.git#v1.4.0-beta.3:plugins/caddy
--build-arg=CADDY_VERSION=2.6.4
-t caddy:2.6.4-with-sablier
```
Then change the image to from `caddy:2.6.4` to `caddy:2.6.4-with-sablier`
```yaml
services:
proxy:
image: caddy:2.6.4-with-sablier
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
whoami:
image: containous/whoami:v1.5.0
sablier:
image: acouvreur/sablier:1.4.0-beta.3
command:
- start
- --provider.name=docker
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
```
### 4. Configure Caddy to use the Sablier Caddy Plugin on the `whoami` service
This is how you opt-in your services and link them with the plugin.
<!-- tabs:start -->
#### **docker-compose.yaml**
```yaml
services:
proxy:
image: caddy:local
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
whoami:
image: containous/whoami:v1.5.0
labels:
- sablier.enable=true
- sablier.group=demo
sablier:
image: acouvreur/sablier:local
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
```
#### **Caddyfile**
```Caddyfile
:80 {
route /whoami {
sablier url=http://sablier:10000 {
group demo
session_duration 1m
dynamic {
display_name My Whoami Service
}
}
reverse_proxy whoami:80
}
}
```
Here we've configured the following things when we're accessing the service on `http://localhost:8080/whoami`:
- The containers that have the label `sablier.group=demo` will be started on demand
- The period of innactivity after which the containers should be shut down is one minute
- It uses the dynamic configuration and configures the title with `My Whoami Service`
<!-- tabs:end -->
?> We've assigned the group `demo` to the service and we use this to identify the workload i

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,13 +1,4 @@
# Sablier Guide: Code-Server + Traefik + Kubernetes Ingress
- [Sablier Guide: Code-Server + Traefik + Kubernetes Ingress](#sablier-guide-code-server--traefik--kubernetes-ingress)
- [1. Prerequisites](#1-prerequisites)
- [2. Create the Kubernetes Cluster using K3S](#2-create-the-kubernetes-cluster-using-k3s)
- [3. Deploy Traefik using Helm](#3-deploy-traefik-using-helm)
- [3. Deploy Sablier](#3-deploy-sablier)
- [4. Deploy Code-Server](#4-deploy-code-server)
- [5. Routing Code-Server through Traefik with the Sablier Plugin Middleware](#5-routing-code-server-through-traefik-with-the-sablier-plugin-middleware)
- [6. Clean up](#6-clean-up)
# Code-Server + Traefik + Kubernetes Ingress
## 1. Prerequisites

5
docs/guides/overview.md Normal file
View File

@@ -0,0 +1,5 @@
# Guides
Guides are here to help you to understand more how to use this API in complex use cases.
Because the nature of Sablier involves multiple reverse proxies and multiple providers, the complexity multiplies.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

23
docs/health.md Normal file
View File

@@ -0,0 +1,23 @@
## Sablier Healthcheck
### Using the `/health` route
You can use the route `/health` to check for healthiness.
- Returns 200 `OK` when ready
- Returns 503 `Service Unavailable` when terminating
### Using the `sablier health` command
You can use the command `sablier health` to check for healthiness.
`sablier health` takes on argument `--url` which defaults to `http://localhost:10000/health`.
```yml
services:
sablier:
image: acouvreur/sablier:1.4.0-beta.3
healthcheck:
test: ["sablier", "health"]
interval: 1m30s
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

59
docs/index.html Normal file
View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sablier - Scale to Zero</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
<style>
:root {
--base-font-size: 16px;
--theme-color : #ffd05b;
}
</style>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'sablier',
repo: 'https://github.com/acouvreur/sablier',
notFoundPage: true,
loadSidebar: true,
alias: {
'/.*/_sidebar.md': '/_sidebar.md'
},
auto2top : true,
maxLevel : 3,
subMaxLevel: 3,
tabs: {
persist : true, // default
sync : true, // default
theme : 'classic', // default
tabComments: true, // default
tabHeadings: true // default
}
}
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/js/docsify-themeable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
<script src="//unpkg.com/prismjs/components/prism-go.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-yaml.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-docker.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-nginx.min.js"></script>
<!-- plugins -->
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="https://unpkg.com/docsify-copy-code@2"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
<script src="//unpkg.com/docsify-pagination/dist/docsify-pagination.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
<script src="//unpkg.com/docsify-plantuml/dist/docsify-plantuml.min.js"></script>
</body>
</html>

42
docs/installation.md Normal file
View File

@@ -0,0 +1,42 @@
# Install Sablier on its own
You can install Sablier with the following flavors:
- Use the Docker image
- Use the binary distribution
- Compile your binary from the sources
## Use the Docker image
- **Docker Hub**: [acouvreur/sablier](https://hub.docker.com/r/acouvreur/sablier)
- **Github Container Registry**: [ghcr.io/acouvreur/sablier](https://github.com/acouvreur/sablier/pkgs/container/sablier)
Choose one of the Docker images and run it with one sample configuration file:
- [sablier.yaml](https://raw.githubusercontent.com/acouvreur/sablier/main/sablier.sample.yaml)
```bash
docker run -d -p 10000:10000 \
-v $PWD/sablier.yml:/etc/sablier/sablier.yml acouvreur/sablier:1.4.0-beta.3
```
## Use the binary distribution
Grab the latest binary from the [releases](https://github.com/acouvreur/sablier/releases) page.
And run it:
```bash
./sablier --help
```
## Compile your binary from the sources
```bash
git clone git@github.com:acouvreur/sablier.git
cd sablier
make
# Output will change depending on your distro
./sablier_draft_linux-amd64
```

88
docs/plugins/caddy.md Normal file
View File

@@ -0,0 +1,88 @@
# Caddy Sablier Plugin
Caddy Sablier Plugin.
## Provider compatibility grid
| Provider | Dynamic | Blocking |
| --------------------------------------- | :-----: | :------: |
| [Docker](/providers/docker) | ✅ | ✅ |
| [Docker Swarm](/providers/docker_swarm) | ✅ | ✅ |
| [Kubernetes](/providers/kubernetes) | ❌ | ❌ |
## Install the plugin to Caddy
Because Caddy does not do runtime evaluation, you need to build the base image with the plugin source code.
In order to use the custom plugin for Caddy, you need to bundle it with Caddy.
Here I'll show you two options with Docker.
<!-- tabs:start -->
#### **Using the provided Dockerfile**
```bash
docker build https://github.com/acouvreur/sablier.git#v1.4.0-beta.3:plugins/caddy
--build-arg=CADDY_VERSION=2.6.4
-t caddy:2.6.4-with-sablier
```
#### **Updating your Caddy Dockerfile**
```docker
ARG CADDY_VERSION=2.6.4
FROM caddy:${CADDY_VERSION}-builder AS builder
ADD https://github.com /acouvreur/sablier.git#v1.4.0-beta.3 /sablier
RUN xcaddy build \
--with github.com/acouvreur/sablier/plugins/caddy=/sablier/plugins/caddy
FROM caddy:${CADDY_VERSION}
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```
<!-- tabs:end -->
## Configuration
You can have the following configuration:
```Caddyfile
:80 {
route /my/route {
sablier [<sablierURL>=http://sablier:10000] {
[names container1,container2,...]
[group mygroup]
[session_duration 30m]
dynamic {
[display_name This is my display name]
[show_details yes|true|on]
[theme hacker-terminal]
[refresh_frequency 2s]
}
blocking {
[timeout 1m]
}
}
reverse_proxy myservice:port
}
}
```
### Exemple with a minimal configuration
Almost all options are optional and you can setup very simple rules to use the server default values.
```Caddyfile
:80 {
route /my/route {
sablier {
group mygroup
dynamic
}
reverse_proxy myservice:port
}
}
```

94
docs/plugins/nginx.md Normal file
View File

@@ -0,0 +1,94 @@
# Nginx Sablier Plugin
The Nginx Sablier Plugin is a javascript file that runs through the NJS Module.
## Provider compatibility grid
| Provider | Dynamic | Blocking |
| --------------------------------------- | :-----: | :------: |
| [Docker](/providers/docker) | ✅ | ✅ |
| [Docker Swarm](/providers/docker_swarm) | ✅ | ✅ |
| [Kubernetes](/providers/kubernetes) | ❌ | ❌ |
## Install the plugin to NGINX
1. Load the `ngx_http_js_module.so` in the main nginx config file `/etc/nginx/nginx.conf`
```nginx
load_module modules/ngx_http_js_module.so;
```
2. Copy/volume the `sablier.js` file to `/etc/nginx/conf.d/sablier.js`
## Configure the plugin
Use this sample for your APIs
```nginx
js_import conf.d/sablier.js;
resolver 127.0.0.11 valid=10s ipv6=off;
server {
listen 80;
subrequest_output_buffer_size 32k;
# The internal location to reach sablier API
set $sablierUrl /sablier;
# Shared variable for default session duration
set $sablierSessionDuration 1m;
# internal location for sablier middleware
# here, the sablier API is a container named "sablier" inside the same network as nginx
location /sablier/ {
internal;
proxy_method GET;
proxy_pass http://sablier:10000/;
}
# A named location that can be used by the sablier middleware to redirect
location @whoami {
# Use variable in order to refresh DNS cache
set $whoami_server whoami;
proxy_pass http://$whoami_server:80;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# The actual location to match your API
# Will answer by a waiting page or redirect to your app if
# it is already up
location /dynamic/whoami {
set $sablierDynamicShowDetails true;
set $sablierDynamicRefreshFrequency 5s;
set $sablierNginxInternalRedirect @whoami;
set $sablierGroup my-group;
set $sablierDynamicName "Dynamic Whoami";
set $sablierDynamicTheme hacker-terminal;
js_content sablier.call;
}
}
```
### Available variables
You can configure the middleware behavior with the following variables:
**General Configuration**
- `set $sablierUrl` The internal routing to reach Sablier API
- `set $sablierGroup` Group name to use to filter by label, ignored if sablierNames is set
- `set $sablierSessionDuration` The session duration after which containers/services/deployments instances are shutdown
- `set $sablierNginxInternalRedirect` The internal location for the service to redirect e.g. @nginx
**Dynamic Configuration**
*if any of these variables is set, then all Blocking Configuration is ignored*
- `set $sablierDynamicName`
- `set $sablierDynamicShowDetails` Set to true or false to show details specifcally for this middleware, unset to use Sablier server defaults
- `set $sablierDynamicTheme` The theme to use
- `set $sablierDynamicRefreshFrequency` The loading page refresh frequency
**Blocking Configuration**
- `set $sablierBlockingTimeout` waits until services are up and running but will not wait more than `timeout`

25
docs/plugins/overview.md Normal file
View File

@@ -0,0 +1,25 @@
# Reverse Proxy Plugins
## What is a Reverse Proxy Plugin ?
Reverse proxy plugins are the integration with a reverse proxy.
?> Because Sablier is designed as an API that can be used on its own, reverse proxy integrations acts as a client of that API.
It leverages the API calls to plugin integration to catch in-flight requests to Sablier.
![Reverse Proxy Integration](../assets/img/reverse-proxy-integration.png)
## Available Reverse Proxies
| Reverse Proxy | Docker | Docker Swarm mode | Kubernetes | Podman |
| ---------------------------- | :----: | :---------------: | :--------: | :-------------------------------------------------------: |
| [Traefik](/plugins/traefik) | ✅ | ✅ | ✅ | [See #70](https://github.com/acouvreur/sablier/issues/70) |
| [Nginx](/plugins/nginx) | ✅ | ✅ | ❌ |
| [Caddy](/plugins/caddy) | ✅ | ✅ | ❌ |
## Runtime and Compiled plugins
Some reverse proxies have the capability to evaluate the plugins at runtime (Traefik with Yaegi, NGINX with Lua and JS plugins) which means the reverse proxy provides a way to consume the plugin directly.
Some others enforce you to rebuild your reverse proxy (Caddy).

160
docs/plugins/traefik.md Normal file
View File

@@ -0,0 +1,160 @@
# Traefik Sablier Plugin
The [Traefik Sablier Plugin](https://plugins.traefik.io/plugins/633b4658a4caa9ddeffda119/sablier) in the plugin catalog.
## Provider compatibility grid
| Provider | Dynamic | Blocking |
| --------------------------------------- | :-----: | :-----------------------------------------------------: |
| [Docker](/providers/docker) | ✅ | ✅ |
| [Docker Swarm](/providers/docker_swarm) | ✅ | ✅ |
| [Kubernetes](/providers/kubernetes) | ✅ | ❌ [#62](https://github.com/acouvreur/sablier/issues/62) |
## Prerequisites
<!-- tabs:start -->
#### **Docker**
**Traefik will evict containers from its pool if they are stopped.**
Meaning labels attached to containers to autodiscover them is not possible with this plugin.
You have to use the dynamic config file provider instead.
**❌ You cannot do the following:**
```yaml
whoami:
image: containous/whoami:v1.5.0
labels:
- traefik.enable
- traefik.http.routers.whoami.rule=PathPrefix(`/whoami`)
- traefik.http.routers.whoami.middlewares=my-sablier@file
```
**✅ You should do the following instead:**
```yaml
http:
services:
whoami:
loadBalancer:
servers:
- url: "http://whoami:80"
routers:
whoami:
rule: PathPrefix(`/whoami`)
entryPoints:
- "http"
middlewares:
- my-sablier@file
service: "whoami"
```
*dynamic-config.yaml*
#### **Docker Swarm**
**Traefik will evict services from its pool if they have 0 replicas.**
In order to use service labels, you have to add the following option on top of each services that will use this plugin.
See also [`traefik.docker.lbswarm`](https://doc.traefik.io/traefik/routing/providers/docker/#traefikdockerlbswarm) label
```yaml
services:
whoami:
image: containous/whoami:v1.5.0
deploy:
replicas: 0
labels:
- traefik.docker.lbswarm=true
```
Traefik also have [allowEmptyServices](https://doc.traefik.io/traefik/providers/docker/#allowemptyservices) option which can be used instead.
But the blocking strategy won't work for the same reasons described in [#62](https://github.com/acouvreur/sablier/issues/62).
#### **Kubernetes**
**Traefik will evict deployments from its pool if they have 0 endpoints available.**
You must use [`allowEmptyServices`](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#allowemptyservices)
The blocking strategy is not supported because everytime the underlying configuration changes, the whole router is regenrated, thus changing the router during a request will still map to the old router. For more details, see [#62](https://github.com/acouvreur/sablier/issues/62).
<!-- tabs:end -->
## Install the plugin to Traefik
<!-- tabs:start -->
#### **File (YAML)**
```yaml
experimental:
plugins:
sablier:
moduleName: "github.com/acouvreur/sablier"
version: "v1.4.0-beta.3"
```
#### **CLI**
```bash
--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier
--experimental.plugins.sablier.version=v1.4.0-beta.3
```
<!-- tabs:end -->
## Configure the plugin using the Dynamic Configuration
<!-- tabs:start -->
#### **File (YAML)**
```yaml
http:
middlewares:
my-sablier:
plugin:
sablier:
group: default
dynamic:
displayName: My Title
refreshFrequency: 5s
showDetails: "true"
theme: hacker-terminal
sablierUrl: http://sablier:10000
sessionDuration: 1m
```
#### **Kubernetes CRD**
```yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: my-sablier
namespace: my-namespace
spec:
plugin:
sablier:
group: default
dynamic:
displayName: My Title
refreshFrequency: 5s
showDetails: "true"
theme: hacker-terminal
sablierUrl: http://sablier:10000
sessionDuration: 1m
```
<!-- tabs:end -->
## Configuration reference
TODO

64
docs/providers/docker.md Normal file
View File

@@ -0,0 +1,64 @@
# Docker
The Docker provider communicates with the `docker.sock` socket to start and stop containers on demand.
## Use the Docker provider
In order to use the docker provider you can configure the [provider.name](TODO) property.
<!-- tabs:start -->
#### **File (YAML)**
```yaml
provider:
name: docker
```
#### **CLI**
```bash
sablier start --provider.name=docker
```
#### **Environment Variable**
```bash
PROVIDER_NAME=docker
```
<!-- tabs:end -->
!> **Ensure that Sablier has access to the docker socket!**
```yaml
services:
sablier:
image: acouvreur/sablier:1.4.0-beta.3
command:
- start
- --provider.name=docker
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
```
## Register containers
For Sablier to work, it needs to know which docker container to start and stop.
You have to register your containers by opting-in with labels.
```yaml
services:
whoami:
image: containous/whoami:v1.5.0
labels:
- sablier.enable=true
- sablier.group=mygroup
```
## How does Sablier knows when a container is ready?
If the container defines a Healthcheck, then it will check for healthiness before stating the `ready` status.
If the containers does not define a Healthcheck, then as soon as the container has the status `started`

View File

@@ -0,0 +1,66 @@
# Docker Swarm
The Docker Swarm provider communicates with the `docker.sock` socket to scale services on demand.
## Use the Docker Swarm provider
In order to use the docker provider you can configure the [provider.name](TODO) property.
<!-- tabs:start -->
#### **File (YAML)**
```yaml
provider:
name: docker_swarm
```
#### **CLI**
```bash
sablier start --provider.name=docker_swarm
```
#### **Environment Variable**
```bash
PROVIDER_NAME=docker_swarm
```
<!-- tabs:end -->
!> **Ensure that Sablier has access to the docker socket!**
```yaml
services:
sablier:
image: acouvreur/sablier:1.4.0-beta.3
command:
- start
- --provider.name=docker_swarm
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
```
## Register services
For Sablier to work, it needs to know which docker services to scale up and down.
You have to register your services by opting-in with labels.
```yaml
services:
whoami:
image: containous/whoami:v1.5.0
deploy:
labels:
- sablier.enable=true
- sablier.group=mygroup
```
## How does Sablier knows when a service is ready?
Sablier checks for the service replicas. As soon as the current replicas matches the wanted replicas, then the service is considered `ready`.
?> Docker Swarm uses the container's healthcheck to check if the container is up and running. So the provider has a native healthcheck support.

View File

@@ -0,0 +1,98 @@
# Kubernetes
Sablier assumes that it is deployed within the Kubernetes cluster to use the Kubernetes API internally.
## Use the Kubernetes provider
In order to use the docker provider you can configure the [provider.name](TODO) property.
<!-- tabs:start -->
#### **File (YAML)**
```yaml
provider:
name: kubernetes
```
#### **CLI**
```bash
sablier start --provider.name=kubernetes
```
#### **Environment Variable**
```bash
PROVIDER_NAME=kubernetes
```
<!-- tabs:end -->
!> **Ensure that Sablier has the necessary roles!**
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: sablier
rules:
- apiGroups:
- apps
- ""
resources:
- deployments
- statefulsets
verbs:
- get # Retrieve info about specific dep
- list # Events
- watch # Events
- apiGroups:
- apps
- ""
resources:
- deployments/scale
- statefulsets/scale
verbs:
- patch # Scale up and down
- update # Scale up and down
- get # Retrieve info about specific dep
- list # Events
- watch # Events
```
## Register Deployments
For Sablier to work, it needs to know which deployments to scale up and down.
You have to register your deployments by opting-in with labels.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
labels:
app: whoami
sablier.enable: "true"
sablier.group: mygroup
spec:
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: containous/whoami:v1.5.0
```
## How does Sablier knows when a deployment is ready?
Sablier checks for the deployment replicas. As soon as the current replicas matches the wanted replicas, then the deployment is considered `ready`.
?> Kubernetes uses the Pod healthcheck to check if the Pod is up and running. So the provider has a native healthcheck support.

View File

@@ -0,0 +1,24 @@
# Providers
## What is a Provider?
A Provider is how Sablier can interact with your instances.
A Provider typically have the following capabilities:
- Start an instance
- Stop an instance
- Get the current status of an instance
- Listen for instance lifecycle events (started, stopped)
## Available providers
| | Provider | Name | Details |
| :---: | --------------------------------------- | -------------- | -------------------------------------------------------- |
| | [Docker](/providers/docker) | `docker` | Stop and start containers on demand |
| | [Docker Swarm](/providers/docker_swarm) | `docker_swarm` | Scale down to zero and up services on demand |
| | [Docker](/providers/kubernetes) | `kubernetes` | Scale down and up deployments and statefulsets on demand |
| | [Podman](/providers/podman) | `podman` | Work in progress |
| | [EC2](/providers/ec2) | `ec2` | Work in progress |
| | [Systemd](/providers/systemd) | `systemd` | Work in progress |
*Your Provider is not on the list? Open an issue or a pull request to add this functionnality here!*

59
docs/strategies.md Normal file
View File

@@ -0,0 +1,59 @@
# Strategies
When configuring
## Dynamic Strategy
The **Dynamic Strategy** provides a waiting page for your session.
?> This strategy is well suited for a user that would access a frontend directly and expects to see a loading page.
```plantuml
@startuml
User -> Proxy: Website Request
Proxy -> Sablier: Reverse Proxy Plugin Request Session Status
Proxy <-- Sablier: Returns the X-Sablier-Status Header
alt `X-Sablier-Status` value is `not-ready`
User <-- Proxy: Serve the waiting page
loop until `X-Sablier-Status` value is `ready`
User -> Proxy: Self-Reload Waiting Page
Proxy -> Sablier: Reverse Proxy Plugin Request Session Status
Sablier -> Provider: Request Instance Status
Sablier <-- Provider: Response Instance Status
Proxy <-- Sablier: Returns the waiting page
User <-- Proxy: Serve the waiting page
end
end
User <-- Proxy: Content
@enduml
```
## Blocking Strategy
The **Blocking Strategy** hangs the request until your session is ready.
?> This strategy is well suited for an API communication.
```plantuml
@startuml
User -> Proxy: Website Request
Proxy -> Sablier: Reverse Proxy Plugin Request Session Status
Sablier -> Provider: Request Instance Status
alt `Instance` status is `not-ready`
end
Sablier <-- Provider: Response Instance Status
Proxy <-- Sablier: Response
User <-- Proxy: Content
@enduml
```

View File

@@ -1,24 +1,28 @@
# Creating your own loading theme
# Themes
Sablier comes with a set of default theme that you can use.
You can also extend the themes by providing your own and they will be templated as Go Templates.
## The embedded themes
| Name | Preview |
| :---------------: | :-------------------------------------------------------------------------------------------------------: |
| `ghost` | ![ghost](https://raw.githubusercontent.com/acouvreur/sablier/main/docs/img/ghost.png) |
| `shuffle` | ![shuffle](https://raw.githubusercontent.com/acouvreur/sablier/main/docs/img/shuffle.png) |
| `hacker-terminal` | ![hacker-terminal](https://raw.githubusercontent.com/acouvreur/sablier/main/docs/img/hacker-terminal.png) |
| `matrix` | ![matrix](https://raw.githubusercontent.com/acouvreur/sablier/main/docs/img/matrix.png) |
- [Creating your own loading theme](#creating-your-own-loading-theme)
- [Custom themes locations](#custom-themes-locations)
- [Create a custom theme](#create-a-custom-theme)
- [Available Go Template Values](#available-go-template-values)
- [The `<meta http-equiv="refresh" />` tag](#the-meta-http-equivrefresh--tag)
- [The `showDetails` option](#the-showdetails-option)
- [The embedded themes](#the-embedded-themes)
- [How to load my custom theme](#how-to-load-my-custom-theme)
- [See the available themes from the API](#see-the-available-themes-from-the-api)
## Custom themes locations
You can use the argument `--strategy.dynamic.coustom-themes` to define the location to search upon starting.
You can use the argument `--strategy.dynamic.custom-themes` to define the location to search upon starting.
By default, the docker image looks for theme inside the `/etc/sablier/themes` folder.
By default, the docker image looks for themes located inside the `/etc/sablier/themes` folder.
```yaml
version: 3.9
services:
sablier:
image: acouvreur/sablier:1.4.0-beta.3
@@ -30,7 +34,6 @@ services:
It will look recursively for themes with the `.html` extension.
- You **cannot** load new themes added in the folder without restarting
- Why? Because we build a theme whitelist in order to prevent malicious payload crafting by using `theme=../../very_secret.txt`
- You **can** modify the existing themes files
## Create a custom theme
@@ -72,10 +75,6 @@ So the first step to create your own theme is to include the `HTML <meta> http-e
If `showDetails` is set to `false` then the `.InstanceStates` will be an empty array.
## The embedded themes
See the [embedded themes](../app/http/pages/themes/).
## How to load my custom theme
You can load themes by specifying their name and their relative path from the `--strategy.dynamic.custom-themes-path` value.
@@ -90,14 +89,14 @@ You can load themes by specifying their name and their relative path from the `-
Such as
```shell
```bash
curl 'http://localhost:10000/api/strategies/dynamic?session_duration=1m&names=nginx&theme=custom1'
```
## See the available themes from the API
```
> curl 'http://localhost:10000/api/strategies/dynamic/themes'
```bash
curl 'http://localhost:10000/api/strategies/dynamic/themes'
```
```json
{

15
docs/versioning.md Normal file
View File

@@ -0,0 +1,15 @@
# Versioning
Sablier follows the [Semantic Versioning 2.0.0](https://semver.org/) Specification (SemVer).
Given a version number MAJOR.MINOR.PATCH, increment the:
1. MAJOR version when you make incompatible API changes
2. MINOR version when you add functionality in a backwards compatible manner
3. PATCH version when you make backwards compatible bug fixes
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
This process is fully automated using [Semantic Release](https://github.com/semantic-release/semantic-release).
The configuration is [release.config.js](https://github.com/acouvreur/sablier/blob/main/release.config.js).