Merge pull request #4384 from dokku/cron-trigger
Add support for injected cron entries from external plugins
This commit is contained in:
@@ -309,6 +309,40 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
|||||||
curl "http://httpstat.us/200"
|
curl "http://httpstat.us/200"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `cron-write`
|
||||||
|
|
||||||
|
- Description: Force triggers writing out cron entries
|
||||||
|
- Invoked by:
|
||||||
|
- Arguments:
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||||
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
```
|
||||||
|
|
||||||
|
### `cron-entries`
|
||||||
|
|
||||||
|
- Description: Allows injecting cron entries into the written out scheduled cron task list. Each entry is newline delimited, and individual entries come in the form `$SCHEDULE;$FULL_COMMAND;$ARBITRARY_DATA`. Individual implementations of cron writing can decide whether and how to include these cron entries. The `ARBITRARY_DATA` includes the log file path for the basic `docker-local` cron implementation.
|
||||||
|
- Invoked by:
|
||||||
|
- Arguments: `$DOKKU_SCHEDULER`
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
|
||||||
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
||||||
|
|
||||||
|
DOKKU_SCHEDULER="$1"
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
```
|
||||||
|
|
||||||
### `dependencies`
|
### `dependencies`
|
||||||
|
|
||||||
- Description: Used to install system-level dependencies.
|
- Description: Used to install system-level dependencies.
|
||||||
|
|||||||
1
plugins/20_events/cron-entries
Symbolic link
1
plugins/20_events/cron-entries
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
hook
|
||||||
1
plugins/20_events/cron-write
Symbolic link
1
plugins/20_events/cron-write
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
hook
|
||||||
1
plugins/cron/.gitignore
vendored
1
plugins/cron/.gitignore
vendored
@@ -2,5 +2,6 @@
|
|||||||
/subcommands/*
|
/subcommands/*
|
||||||
/triggers/*
|
/triggers/*
|
||||||
/triggers
|
/triggers
|
||||||
|
/cron-*
|
||||||
/post-*
|
/post-*
|
||||||
/report
|
/report
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
SUBCOMMANDS = subcommands/list subcommands/report
|
SUBCOMMANDS = subcommands/list subcommands/report
|
||||||
TRIGGERS = triggers/post-delete triggers/post-deploy triggers/report
|
TRIGGERS = triggers/cron-write triggers/post-delete triggers/post-deploy triggers/report
|
||||||
BUILD = commands subcommands triggers
|
BUILD = commands subcommands triggers
|
||||||
PLUGIN_NAME = cron
|
PLUGIN_NAME = cron
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
appjson "github.com/dokku/dokku/plugins/app-json"
|
appjson "github.com/dokku/dokku/plugins/app-json"
|
||||||
"github.com/dokku/dokku/plugins/common"
|
"github.com/dokku/dokku/plugins/common"
|
||||||
@@ -17,10 +17,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type templateCommand struct {
|
type templateCommand struct {
|
||||||
ID string
|
ID string
|
||||||
App string
|
App string
|
||||||
Command string
|
Command string
|
||||||
Schedule string
|
Schedule string
|
||||||
|
AltCommand string
|
||||||
|
LogFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t templateCommand) CronCommand() string {
|
||||||
|
if t.AltCommand != "" {
|
||||||
|
if t.LogFile != "" {
|
||||||
|
return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile)
|
||||||
|
}
|
||||||
|
return t.AltCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("dokku --rm run --cron-id %s %s", t.ID, t.App, t.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchCronEntries(appName string) ([]templateCommand, error) {
|
func fetchCronEntries(appName string) ([]templateCommand, error) {
|
||||||
@@ -86,11 +99,7 @@ func deleteCrontab() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeCronEntries() error {
|
func writeCronEntries() error {
|
||||||
apps, err := common.DokkuApps()
|
apps, _ := common.DokkuApps()
|
||||||
if err != nil {
|
|
||||||
return deleteCrontab()
|
|
||||||
}
|
|
||||||
|
|
||||||
commands := []templateCommand{}
|
commands := []templateCommand{}
|
||||||
for _, appName := range apps {
|
for _, appName := range apps {
|
||||||
scheduler := common.GetAppScheduler(appName)
|
scheduler := common.GetAppScheduler(appName)
|
||||||
@@ -106,6 +115,30 @@ func writeCronEntries() error {
|
|||||||
commands = append(commands, c...)
|
commands = append(commands, c...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b, _ := common.PlugnTriggerOutput("cron-entries", "docker-local")
|
||||||
|
common.Log(fmt.Sprintf("output: %s", string(b)))
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(string(b[:])), "\n") {
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(line, ";")
|
||||||
|
if len(parts) != 2 && len(parts) != 3 {
|
||||||
|
return fmt.Errorf("Invalid injected cron task: %v", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
id := base64.StdEncoding.EncodeToString([]byte(strings.Join(parts, ";;;")))
|
||||||
|
command := templateCommand{
|
||||||
|
ID: id,
|
||||||
|
Schedule: parts[0],
|
||||||
|
AltCommand: parts[1],
|
||||||
|
}
|
||||||
|
if len(parts) == 3 {
|
||||||
|
command.LogFile = parts[2]
|
||||||
|
}
|
||||||
|
commands = append(commands, command)
|
||||||
|
}
|
||||||
|
|
||||||
if len(commands) == 0 {
|
if len(commands) == 0 {
|
||||||
return deleteCrontab()
|
return deleteCrontab()
|
||||||
}
|
}
|
||||||
@@ -119,7 +152,7 @@ func writeCronEntries() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "CopyFromImage"))
|
tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronEntries"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot create temporary schedule file: %v", err)
|
return fmt.Errorf("Cannot create temporary schedule file: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ func main() {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch trigger {
|
switch trigger {
|
||||||
|
case "cron-write":
|
||||||
|
err = cron.TriggerCronWrite()
|
||||||
case "post-delete":
|
case "post-delete":
|
||||||
err = cron.TriggerPostDelete()
|
err = cron.TriggerPostDelete()
|
||||||
case "post-deploy":
|
case "post-deploy":
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ PATH=/usr/local/bin:/usr/bin:/bin
|
|||||||
SHELL=/bin/bash
|
SHELL=/bin/bash
|
||||||
|
|
||||||
{{ range $entry := .Commands -}}
|
{{ range $entry := .Commands -}}
|
||||||
{{ $entry.Schedule }} dokku --rm run --cron-id {{ $entry.ID }} {{ $entry.App }} {{ $entry.Command }}
|
{{ $entry.Schedule }} {{ $entry.CronCommand }}
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
|||||||
@@ -9,3 +9,8 @@ func TriggerPostDelete() error {
|
|||||||
func TriggerPostDeploy() error {
|
func TriggerPostDeploy() error {
|
||||||
return writeCronEntries()
|
return writeCronEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TriggerCronWrite force updates the cron file for all apps
|
||||||
|
func TriggerCronWrite() error {
|
||||||
|
return writeCronEntries()
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,20 @@ load test_helper
|
|||||||
setup() {
|
setup() {
|
||||||
global_setup
|
global_setup
|
||||||
create_app
|
create_app
|
||||||
|
mkdir -p /var/lib/dokku/plugins/available/cron-entries
|
||||||
|
mkdir -p /var/lib/dokku/plugins/enabled/cron-entries
|
||||||
|
cat >"/var/lib/dokku/plugins/available/cron-entries/plugin.toml" <<EOF
|
||||||
|
[plugin]
|
||||||
|
description = "dokku test cron-entries plugin"
|
||||||
|
version = "0.0.1"
|
||||||
|
[plugin.config]
|
||||||
|
EOF
|
||||||
|
cp /var/lib/dokku/plugins/available/cron-entries/plugin.toml /var/lib/dokku/plugins/enabled/cron-entries/plugin.toml
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown() {
|
teardown() {
|
||||||
|
rm -rf /var/lib/dokku/plugins/available/cron-entries /var/lib/dokku/plugins/enabled/cron-entries
|
||||||
destroy_app
|
destroy_app
|
||||||
global_teardown
|
global_teardown
|
||||||
}
|
}
|
||||||
@@ -99,6 +110,69 @@ teardown() {
|
|||||||
assert_output_contains "python task.py second"
|
assert_output_contains "python task.py second"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "(cron) injected entries" {
|
||||||
|
echo "echo '@daily;/bin/true'" > /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
chmod +x /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger cron-write"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
|
||||||
|
run /bin/bash -c "cat /var/spool/cron/crontabs/dokku"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "@daily /bin/true"
|
||||||
|
|
||||||
|
# log file
|
||||||
|
echo "echo '@daily;/bin/true;/var/log/dokku/log.log'" > /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
chmod +x /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger cron-write"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
|
||||||
|
run /bin/bash -c "cat /var/spool/cron/crontabs/dokku"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "@daily /bin/true &>> /var/log/dokku/log.log"
|
||||||
|
|
||||||
|
# specify matching scheduler
|
||||||
|
echo "[[ \$1 == 'docker-local' ]] && echo '@daily;/bin/true'" > /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
chmod +x /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger cron-write"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
|
||||||
|
run /bin/bash -c "cat /var/spool/cron/crontabs/dokku"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "@daily /bin/true"
|
||||||
|
|
||||||
|
# specify non-matching scheduler
|
||||||
|
echo "[[ \$1 == 'kubernetes' ]] && echo '@daily;/bin/true'" > /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
chmod +x /var/lib/dokku/plugins/enabled/cron-entries/cron-entries
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger cron-write"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
|
||||||
|
run /bin/bash -c "cat /var/spool/cron/crontabs/dokku"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
|
||||||
template_cron_file_invalid() {
|
template_cron_file_invalid() {
|
||||||
local APP="$1"
|
local APP="$1"
|
||||||
|
|||||||
Reference in New Issue
Block a user