Merge pull request #4384 from dokku/cron-trigger

Add support for injected cron entries from external plugins
This commit is contained in:
Jose Diaz-Gonzalez
2021-02-07 19:28:16 -05:00
committed by GitHub
10 changed files with 164 additions and 13 deletions

View File

@@ -309,6 +309,40 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
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`
- Description: Used to install system-level dependencies.

View File

@@ -0,0 +1 @@
hook

View File

@@ -0,0 +1 @@
hook

View File

@@ -2,5 +2,6 @@
/subcommands/*
/triggers/*
/triggers
/cron-*
/post-*
/report

View File

@@ -1,5 +1,5 @@
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
PLUGIN_NAME = cron

View File

@@ -4,11 +4,11 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/template"
appjson "github.com/dokku/dokku/plugins/app-json"
"github.com/dokku/dokku/plugins/common"
@@ -17,10 +17,23 @@ import (
)
type templateCommand struct {
ID string
App string
Command string
Schedule string
ID string
App string
Command 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) {
@@ -86,11 +99,7 @@ func deleteCrontab() error {
}
func writeCronEntries() error {
apps, err := common.DokkuApps()
if err != nil {
return deleteCrontab()
}
apps, _ := common.DokkuApps()
commands := []templateCommand{}
for _, appName := range apps {
scheduler := common.GetAppScheduler(appName)
@@ -106,6 +115,30 @@ func writeCronEntries() error {
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 {
return deleteCrontab()
}
@@ -119,7 +152,7 @@ func writeCronEntries() error {
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 {
return fmt.Errorf("Cannot create temporary schedule file: %v", err)
}

View File

@@ -18,6 +18,8 @@ func main() {
var err error
switch trigger {
case "cron-write":
err = cron.TriggerCronWrite()
case "post-delete":
err = cron.TriggerPostDelete()
case "post-deploy":

View File

@@ -2,5 +2,5 @@ PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
{{ range $entry := .Commands -}}
{{ $entry.Schedule }} dokku --rm run --cron-id {{ $entry.ID }} {{ $entry.App }} {{ $entry.Command }}
{{ $entry.Schedule }} {{ $entry.CronCommand }}
{{ end -}}

View File

@@ -9,3 +9,8 @@ func TriggerPostDelete() error {
func TriggerPostDeploy() error {
return writeCronEntries()
}
// TriggerCronWrite force updates the cron file for all apps
func TriggerCronWrite() error {
return writeCronEntries()
}

View File

@@ -5,9 +5,20 @@ load test_helper
setup() {
global_setup
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() {
rm -rf /var/lib/dokku/plugins/available/cron-entries /var/lib/dokku/plugins/enabled/cron-entries
destroy_app
global_teardown
}
@@ -99,6 +110,69 @@ teardown() {
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() {
local APP="$1"