Skip to content
This repository has been archived by the owner on Jul 7, 2021. It is now read-only.

Support Caddy v2 via JSON configuration #16

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.15.x, 1.16.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2

- name: Get dependencies
run: go get -v -t -d ./...
- name: Test
run: go test ./...
14 changes: 0 additions & 14 deletions .travis.yml

This file was deleted.

183 changes: 96 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,55 @@
# caddy-supervisor [![Build Status](https://travis-ci.org/lucaslorentz/caddy-supervisor.svg?branch=master)](https://travis-ci.org/lucaslorentz/caddy-supervisor)
# Caddy Supervisor

## Introduction
This plugin enables caddy to run and supervise background processes.
[![Build Status](https://github.com/baldinof/caddy-supervisor/actions/workflows/ci.yaml/badge.svg)](hthttps://github.com/Baldinof/caddy-supervisor/actions/workflows/ci.yaml)

## How it works
For every **supervisor** caddy directive a command is executed in background and killed when caddy stops.

You can use **supervisor** plugin as an http directive or as a server type.
A module to run and supervise background processes from Caddy

## Supervisor http directive
You can activate a supervisor directly from your web caddyfile using:
```
supervisor command arg1 arg2 arg3
```
## How it works

Or using a block for more control
```
supervisor {
command command
args arg1 arg2 arg3
dir directory
env VARIABLEA VALUEA
env VARIABLEB VALUEB
redirect_stdout file
redirect_stderr file
restart_policy policy
termination_grace_period period
replicas replicas
For every process in the **supervisor** caddyfile directive a command is executed in background and killed when caddy stops.

## Full HTTP Cadyfile example

```Caddyfile
{
# Must be in global options
supervisor {
php-fpm --no-daemonize {
dir /path/to/desired/working-dir # default to current dir

env APP_ENV production
env DEBUG false

restart_policy always # default to 'always', other values allowed: 'never', 'on_failure'

redirect_stdout file /var/log/fpm.log # redirect command stdout to a file. Default to caddy `stdout`
redirect_stderr file /var/log/fpm-error.log # redirect command stderr to a file. Default to caddy `stderr`

termination_grace_period 30s # default to '10s', amount of time to wait for application graceful termination before killing it

replicas 3 # default to 1, number of instances that should be executed
}

# block configuration is optional
node worker.js
}
}
```

## Supervisor server type
You can also use a supervisor server type using `-type` CLI option:
```
caddy -type supervisor
```

The Caddyfile syntax for supervisor server type is:
```
name {
command command
args arg1 arg2 arg3
dir directory
env VARIABLEA VALUEA
env VARIABLEB VALUEB
redirect_stdout file
redirect_stderr file
restart_policy policy
termination_grace_period period
replicas replicas
}
...
mysite.com
```

## Options description

- **command**: the command or executable name to be executed. Supports template.
- **args**: args provided to the command, separated by whitespace. Supports template.
- **dir**: the working directory the command should be executed in. Supports template.
- **env**: declare environment variable that should be passed to command. This property can be repeated. Supports template.
- **redirect_stdout**: redirect command stdout to a file. Use "stdout" to redirect to caddy stdout
- **redirect_stderr**: redirect command stderr to a file. Use "stderr" to redirect to caddy stderr
- **restart_policy**: define under which conditions the command should be restarted after exit. Valid values:
- **command**: the command to be executed. _Supports template_.
- **dir**: the working directory the command should be executed in. _Supports template_.
- **env**: declare environment variable that should be passed to command. This property can be repeated. _Supports template_.
- **redirect_stdout**: redirect command stdout. Default: `stdout`, Possible values:
- **null**: discard output
- **stdout**: redirect to the caddy process stdout
- **stderr**: redirect to the caddy process stderr
- **file /path/to/file**: redirect output to a file
- **redirect_stderr**: redirect command stderr. Default: `stderr`, See above for possible values.
- **restart_policy**: define under which conditions the command should be restarted after exit. Default: `always` Valid values:
- **never**: do not restart the command
- **on_failure**: restart if exit code is not 0
- **always**: always restart
Expand All @@ -71,7 +59,7 @@ name {
On windows **termination_grace_period** is ignored and the command is killed immediatelly due to lack of signals support.

## Templates
To enable different configuration per replica, you can use go templates on the fields marked with Supports template".
To enable different configuration per replica, you can use go templates on the fields marked with _Supports template_".

The following information are available to templates:
- **Replica**: the index of the current replica, starting from 0
Expand All @@ -80,10 +68,15 @@ Templates also supports all functions from http://masterminds.github.io/sprig/

Example:
```
supervisor myapp --port "{{add 8000 .Replica}}" {
replicas 5
{
supervisor {
myapp --port "{{add 8000 .Replica}}" {
replicas 5
}
}
}
proxy / localhost:8000-8004

reverse_proxy * localhost:8000-8004
```

## Exponential backoff
Expand All @@ -94,44 +87,60 @@ That means that when the command fail, it will be restarted with a delay of 0 se
If the command runs stable for at least 10 minutes, the restart delay is reset to 0 seconds.

## Examples
AspNet Core application on windows:
```
example.com {
run {
env ASPNETCORE_URLS http://localhost:5000
command dotnet ./MyApplication.dll
dir "C:\MyApplicationFolder"
redirect_stdout stdout
redirect_stderr stderr
restart_policy always
}
proxy / localhost:5000 {
transparent

PHP server (useful if you want a single container PHP application with php-fpm):

```Caddyfile
{
supervisor {
php-fpm
}
}

example.com

php_fastcgi 127.0.0.1:9000
root * .
encode gzip
file_server
```

Php fastcgi on windows:
AspNet Core application on windows:

```
example.com {
run {
command ./php-cgi.exe
args -b 9800
dir C:/php/
redirect_stdout stdout
redirect_stderr stderr
restart_policy always
{
supervisor {
dotnet ./MyApplication.dll {
env ASPNETCORE_URLS http://localhost:5000
dir "C:\MyApplicationFolder"
redirect_stdout stdout
redirect_stderr stderr
restart_policy always
}
}
root C:/Site
fastcgi / localhost:9800 php
}

example.com

reverse_proxy localhost:5000
```

## Building it
Build from caddy repository and import **caddy-supervisor** plugin on file https://github.com/mholt/caddy/blob/master/caddy/caddymain/run.go :

Use the `xcaddy` tool to build a version of caddy with this module:

```
import (
_ "github.com/lucaslorentz/caddy-supervisor/httpplugin"
_ "github.com/lucaslorentz/caddy-supervisor/servertype"
)
xcaddy build \
--with github.com/baldinof/caddy-supervisor
```

## Todo

- Dedicated Caddyfile support (Supervisorfile)
- Send processes outputs to Caddy logs

## Credits

This package is continuation of https://github.com/lucaslorentz/caddy-supervisor which only supports Caddy v1.

Thank you @lucaslorentz for the original work ❤️
71 changes: 71 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package supervisor

import (
"github.com/caddyserver/caddy/v2"
"go.uber.org/zap"
)

// Interface guards
var (
_ caddy.App = (*App)(nil)
_ caddy.Module = (*App)(nil)
_ caddy.Provisioner = (*App)(nil)
)

func init() {
caddy.RegisterModule(App{})
}

type App struct {
Supervise []Definition `json:"supervise,omitempty"`
log *zap.Logger
supervisors []*Supervisor
}

// CaddyModule implements caddy.Module
func (a App) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "supervisor",
New: func() caddy.Module { return new(App) },
}
}

// Provision implements caddy.Provisioner
func (a *App) Provision(context caddy.Context) error {
a.log = context.Logger(a)

for _, definition := range a.Supervise {

supervisors, err := definition.ToSupervisors(a.log)

if err != nil {
return err
}

a.supervisors = append(a.supervisors, supervisors...)
}

a.log.Debug("module provisioned", zap.Any("supervisors", a.supervisors))

return nil
}

// Start implements caddy.App
func (a *App) Start() error {
for _, s := range a.supervisors {
go s.Run()
}

a.log.Debug("module started")

return nil
}

// Stop implements caddy.App
func (a *App) Stop() error {
for _, s := range a.supervisors {
s.Stop()
}

return nil
}
41 changes: 0 additions & 41 deletions build.sh

This file was deleted.

Loading