feat: add the ability to specify a custom mailto for all cron output

This commit is contained in:
Jose Diaz-Gonzalez
2023-08-19 23:59:00 -04:00
parent a433508fb1
commit c23e878e7d
12 changed files with 187 additions and 5 deletions

View File

@@ -549,18 +549,18 @@ echo "clock: some-command" >> Procfile
popd &>/dev/null
```
### `cron-write`
### `cron-get-property`
- Description: Force triggers writing out cron entries
- Description: Return the value for an app's cron property
- Invoked by:
- Arguments:
- Arguments: `$APP $KEY`
- Example:
```shell
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
APP="$1"; PROPERTY="$2"
# TODO
```
@@ -583,6 +583,22 @@ DOKKU_SCHEDULER="$1"
# TODO
```
### `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
```
### `dependencies`
- Description: Used to install system-level dependencies.

View File

@@ -46,10 +46,27 @@ When running scheduled cron tasks, there are a few items to be aware of:
- Scheduled cron tasks are performed within the app environment available at runtime. If the app image does not exist, the command may fail to execute.
- Schedules are performed on the hosting server's timezone, which is typically UTC.
- At this time, only the `PATH` and `SHELL` environment variables are specified in the cron template.
- A `MAILTO` value can be set via the `cron:set` command.
- Each scheduled task is executed within a one-off `run` container, and thus inherit any docker-options specified for `run` containers. Resources are never shared between scheduled tasks.
- Scheduled cron tasks are supported on a per-scheduler basis, and are currently only implemented by the `docker-local` scheduler.
- Tasks for _all_ apps managed by the `docker-local` scheduler are written to a single crontab file owned by the `dokku` user. The `dokku` user's crontab should be considered reserved for this purpose.
#### Specifying a MAILTO value
By default, cron tasks complete and do not perform any reporting. Users can specify a value for `MAILTO` via the global `mailto` cron property by using the `cron:set` command.
```shell
dokku cron:set --global mailto example@example.com
```
All output for individual cron runs will be sent to the specified email.
Cron emails can be disabled by running the `cron:set` command for the global `mailto` property with no value.
```shell
dokku cron:set --global mailto
```
#### Listing Cron tasks
Cron tasks for an app can be listed via the `cron:list` command. This command takes an `app` argument.

View File

@@ -0,0 +1 @@
hook

View File

@@ -1,5 +1,6 @@
SUBCOMMANDS = subcommands/list subcommands/report subcommands/run
BUILD = commands subcommands
TRIGGERS = triggers/cron-get-property
BUILD = commands subcommands triggers
PLUGIN_NAME = cron
include ../../common.mk

View File

@@ -13,6 +13,18 @@ import (
cronparser "github.com/robfig/cron/v3"
)
var (
// DefaultProperties is a map of all valid network properties with corresponding default property values
DefaultProperties = map[string]string{
"mailto": "",
}
// GlobalProperties is a map of all valid global network properties
GlobalProperties = map[string]bool{
"mailto": true,
}
)
type TemplateCommand struct {
ID string `json:"id"`
App string `json:"app"`

View File

@@ -13,6 +13,7 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
}
flags := map[string]common.ReportFunc{
"--cron-mailto": reportMailto,
"--cron-task-count": reportTasks,
}
@@ -27,6 +28,10 @@ func ReportSingleApp(appName string, format string, infoFlag string) error {
return common.ReportSingleApp("cron", appName, infoFlag, infoFlags, flagKeys, format, trimPrefix, uppercaseFirstCharacter)
}
func reportMailto(_ string) string {
return common.PropertyGet("cron", "--global", "mailto")
}
func reportTasks(appName string) string {
c, _ := FetchCronEntries(appName)
return strconv.Itoa(len(c))

11
plugins/cron/set.go Normal file
View File

@@ -0,0 +1,11 @@
package cron
import "errors"
func validateSetValue(appName string, key string, value string) error {
if key == "mailto" && appName != "--global" {
return errors.New("Property cannot be specified on a per-app basis")
}
return nil
}

View File

@@ -0,0 +1,48 @@
package main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/dokku/dokku/plugins/common"
"github.com/dokku/dokku/plugins/cron"
)
// main entrypoint to all triggers
func main() {
parts := strings.Split(os.Args[0], "/")
trigger := parts[len(parts)-1]
flag.Parse()
var err error
switch trigger {
case "cron-get-property":
appName := flag.Arg(0)
property := flag.Arg(1)
err = cron.TriggerCronGetProperty(appName, property)
case "install":
err = cron.TriggerInstall()
case "post-app-clone-setup":
oldAppName := flag.Arg(0)
newAppName := flag.Arg(1)
err = cron.TriggerPostAppCloneSetup(oldAppName, newAppName)
case "post-app-rename-setup":
oldAppName := flag.Arg(0)
newAppName := flag.Arg(1)
err = cron.TriggerPostAppRenameSetup(oldAppName, newAppName)
case "post-delete":
appName := flag.Arg(0)
err = cron.TriggerPostDelete(appName)
case "report":
appName := flag.Arg(0)
err = cron.ReportSingleApp(appName, "", "")
default:
err = fmt.Errorf("Invalid plugin trigger call: %s", trigger)
}
if err != nil {
common.LogFailWithError(err)
}
}

View File

@@ -112,3 +112,13 @@ func CommandRun(appName string, cronID string, detached bool) error {
args := append([]string{scheduler, appName, "0"}, fields...)
return common.PlugnTrigger("scheduler-run", args...)
}
// CommandSet set or clear a cron property for an app
func CommandSet(appName string, property string, value string) error {
if err := validateSetValue(appName, property, value); err != nil {
return err
}
common.CommandPropertySet("cron", appName, property, value, DefaultProperties, GlobalProperties)
return common.PlugnTrigger("cron-write")
}

View File

@@ -1,3 +1,6 @@
{{ if .Mailto -}}
MAILTO={{ .Mailto }}
{{ end -}}
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

55
plugins/cron/triggers.go Normal file
View File

@@ -0,0 +1,55 @@
package cron
import (
"errors"
"fmt"
"github.com/dokku/dokku/plugins/common"
)
// TriggerCronGetProperty writes the cron key to stdout for a given app container
func TriggerCronGetProperty(appName string, key string) error {
if key != "mailto" {
return errors.New("Invalid cron property specified")
}
fmt.Println(common.PropertyGet("cron", appName, key))
return nil
}
// TriggerInstall runs the install step for the cron plugin
func TriggerInstall() error {
if err := common.PropertySetup("cron"); err != nil {
return fmt.Errorf("Unable to install the cron plugin: %s", err.Error())
}
return nil
}
// TriggerPostAppCloneSetup creates new cron files
func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error {
err := common.PropertyClone("cron", oldAppName, newAppName)
if err != nil {
return err
}
return nil
}
// TriggerPostAppRenameSetup renames cron files
func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error {
if err := common.PropertyClone("cron", oldAppName, newAppName); err != nil {
return err
}
if err := common.PropertyDestroy("cron", oldAppName); err != nil {
return err
}
return nil
}
// TriggerPostDelete destroys the cron property for a given app container
func TriggerPostDelete(appName string) error {
return common.PropertyDestroy("cron", appName)
}

View File

@@ -116,8 +116,11 @@ func writeCronEntries() error {
return deleteCrontab()
}
mailto, _ := common.PlugnTriggerOutputAsString("cron-get-property", []string{"--global", "mailto"}...)
data := map[string]interface{}{
"Commands": commands,
"Mailto": mailto,
}
t, err := getCronTemplate()