Merge pull request #8082 from dokku/rename-cron-code
Standardize naming in cron code
This commit is contained in:
@@ -4,5 +4,5 @@
|
|||||||
|
|
||||||
- The `plugin:list` command no longer outputs the version for the `plugn` binary.
|
- The `plugin:list` command no longer outputs the version for the `plugn` binary.
|
||||||
- Users building docker images that run Dokku will need to use a new sudoer wrapper for the `docker-image-labeler` binary to work correctly. A reference version has been placed in the `docker` skeleton directory. This should only impact platform developers, and users of our Docker image will already have the file available.
|
- Users building docker images that run Dokku will need to use a new sudoer wrapper for the `docker-image-labeler` binary to work correctly. A reference version has been placed in the `docker` skeleton directory. This should only impact platform developers, and users of our Docker image will already have the file available.
|
||||||
- The `dokku` user's cron is now in use by Dokku itself. Customizations will be overwritten. Users are encouraged to use a cron entry in `/etc/cron.d/dokku` to avoid this issue.
|
- The `dokku` user's cron is now in use by Dokku itself. Customizations will be overwritten. Users are encouraged to use a cron file at `/etc/cron.d/dokku` to avoid this issue.
|
||||||
- As of 0.23.0, Dokku will now inject the `max-size` log driver option for applications. This is restricted to app-configured log driver values empty, `local` or `json-file` in 0.23.1 to increase setup compatibility. Users who configure alternative log drivers at the system level will need to either set the global `max-size` property to `unlimited` or switch to the built-in `vector` logging support.
|
- As of 0.23.0, Dokku will now inject the `max-size` log driver option for applications. This is restricted to app-configured log driver values empty, `local` or `json-file` in 0.23.1 to increase setup compatibility. Users who configure alternative log drivers at the system level will need to either set the global `max-size` property to `unlimited` or switch to the built-in `vector` logging support.
|
||||||
|
|||||||
@@ -582,7 +582,7 @@ APP="$1"; PROPERTY="$2"
|
|||||||
|
|
||||||
### `cron-entries`
|
### `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.
|
- Description: Allows injecting cron tasks into the written out scheduled cron task list. Each entry is newline delimited, and individual tasks come in the form `$SCHEDULE;$FULL_COMMAND;$ARBITRARY_DATA`. Individual implementations of cron writing can decide whether and how to include these cron tasks. The `ARBITRARY_DATA` includes the log file path for the basic `docker-local` cron implementation.
|
||||||
- Invoked by:
|
- Invoked by:
|
||||||
- Arguments: `$DOKKU_SCHEDULER`
|
- Arguments: `$DOKKU_SCHEDULER`
|
||||||
- Example:
|
- Example:
|
||||||
@@ -2450,7 +2450,7 @@ DOKKU_SCHEDULER="$1"; APP="$2";
|
|||||||
> The scheduler plugin trigger apis are under development and may change
|
> The scheduler plugin trigger apis are under development and may change
|
||||||
> between minor releases until the 1.0 release.
|
> between minor releases until the 1.0 release.
|
||||||
|
|
||||||
- Description: Force triggers writing out cron entries. Arguments are optional.
|
- Description: Force triggers writing out cron tasks. Arguments are optional.
|
||||||
- Invoked by: `ps:start`, `ps:stop`, `cron:set`
|
- Invoked by: `ps:start`, `ps:stop`, `cron:set`
|
||||||
- Arguments: `$DOKKU_SCHEDULER $APP`
|
- Arguments: `$DOKKU_SCHEDULER $APP`
|
||||||
- Example:
|
- Example:
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ The `app.json` file for a given app can define a special `cron` key that contain
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A cron entry takes the following properties:
|
A cron task takes the following properties:
|
||||||
|
|
||||||
- `command`: A command to be run within the built app image. Specified commands can also be `Procfile` entries.
|
- `command`: A command to be run within the built app image. Specified commands can also be `Procfile` entries.
|
||||||
- `schedule`: A [cron-compatible](https://en.wikipedia.org/wiki/Cron#Overview) scheduling definition upon which to run the command. Seconds are generally not supported.
|
- `schedule`: A [cron-compatible](https://en.wikipedia.org/wiki/Cron#Overview) scheduling definition upon which to run the command. Seconds are generally not supported.
|
||||||
|
|
||||||
Zero or more cron commands can be specified per app. Cron entries are validated after the build artifact is created but before the app is deployed, and the cron schedule is updated during the post-deploy phase.
|
Zero or more cron tasks can be specified per app. Cron tasks are validated after the build artifact is created but before the app is deployed, and the cron schedule is updated during the post-deploy phase.
|
||||||
|
|
||||||
See the [app.json location documentation](/docs/advanced-usage/deployment-tasks.md#changing-the-appjson-location) for more information on where to place your `app.json` file.
|
See the [app.json location documentation](/docs/advanced-usage/deployment-tasks.md#changing-the-appjson-location) for more information on where to place your `app.json` file.
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ For tasks that will properly resume, you _should_ use the above method, as runni
|
|||||||
|
|
||||||
Regularly scheduled tasks can be a bit of a pain with Dokku. The following are general recommendations to follow to help ensure successful task runs.
|
Regularly scheduled tasks can be a bit of a pain with Dokku. The following are general recommendations to follow to help ensure successful task runs.
|
||||||
|
|
||||||
- Use the `dokku` user in your cron entry.
|
- Use the `dokku` user in your cron task.
|
||||||
- If you do not, the `dokku` binary will attempt to execute with `sudo`, and your cron run with fail with `sudo: no tty present and no askpass program specified`.
|
- If you do not, the `dokku` binary will attempt to execute with `sudo`, and your cron run with fail with `sudo: no tty present and no askpass program specified`.
|
||||||
- Add a `MAILTO` environment variable to ship cron emails to yourself.
|
- Add a `MAILTO` environment variable to ship cron emails to yourself.
|
||||||
- Add a `PATH` environment variable or specify the full path to binaries on the host.
|
- Add a `PATH` environment variable or specify the full path to binaries on the host.
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ var (
|
|||||||
|
|
||||||
// AppJSON is a struct that represents an app.json file as understood by Dokku
|
// AppJSON is a struct that represents an app.json file as understood by Dokku
|
||||||
type AppJSON struct {
|
type AppJSON struct {
|
||||||
// Cron is a list of cron commands to execute
|
// Cron is a list of cron tasks to execute
|
||||||
Cron []CronCommand `json:"cron"`
|
Cron []CronTask `json:"cron"`
|
||||||
|
|
||||||
// Formation is a map of process types to scale
|
// Formation is a map of process types to scale
|
||||||
Formation map[string]Formation `json:"formation"`
|
Formation map[string]Formation `json:"formation"`
|
||||||
@@ -50,8 +50,8 @@ type AppJSON struct {
|
|||||||
} `json:"scripts"`
|
} `json:"scripts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CronCommand is a struct that represents a single cron command from an app.json file
|
// CronTask is a struct that represents a single cron task from an app.json file
|
||||||
type CronCommand struct {
|
type CronTask struct {
|
||||||
// Command is the command to execute
|
// Command is the command to execute
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
|
|
||||||
|
|||||||
@@ -27,18 +27,18 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemplateCommand is a struct that represents a cron command
|
// CronTask is a struct that represents a cron task
|
||||||
type TemplateCommand struct {
|
type CronTask struct {
|
||||||
// ID is a unique identifier for the cron command
|
// ID is a unique identifier for the cron task
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
||||||
// App is the app the cron command belongs to
|
// App is the app the cron task belongs to
|
||||||
App string `json:"app,omitempty"`
|
App string `json:"app,omitempty"`
|
||||||
|
|
||||||
// Command is the command to run
|
// Command is the command to run
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
|
|
||||||
// Global is whether the cron command is global
|
// Global is whether the cron task is global
|
||||||
Global bool `json:"global,omitempty"`
|
Global bool `json:"global,omitempty"`
|
||||||
|
|
||||||
// Schedule is the cron schedule
|
// Schedule is the cron schedule
|
||||||
@@ -50,12 +50,12 @@ type TemplateCommand struct {
|
|||||||
// LogFile is the log file to write to
|
// LogFile is the log file to write to
|
||||||
LogFile string `json:"-"`
|
LogFile string `json:"-"`
|
||||||
|
|
||||||
// Maintenance is whether the cron command is in maintenance mode
|
// Maintenance is whether the cron task is in maintenance mode
|
||||||
Maintenance bool `json:"maintenance"`
|
Maintenance bool `json:"maintenance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CronCommand returns the command to run for a given cron command
|
// DokkuRunCommand returns the dokku run command to execute for a given cron task
|
||||||
func (t TemplateCommand) CronCommand() string {
|
func (t CronTask) DokkuRunCommand() string {
|
||||||
if t.AltCommand != "" {
|
if t.AltCommand != "" {
|
||||||
if t.LogFile != "" {
|
if t.LogFile != "" {
|
||||||
return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile)
|
return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile)
|
||||||
@@ -66,17 +66,17 @@ func (t TemplateCommand) CronCommand() string {
|
|||||||
return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command)
|
return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchCronEntriesInput is the input for the FetchCronEntries function
|
// FetchCronTasksInput is the input for the FetchCronTasks function
|
||||||
type FetchCronEntriesInput struct {
|
type FetchCronTasksInput struct {
|
||||||
AppName string
|
AppName string
|
||||||
AppJSON *appjson.AppJSON
|
AppJSON *appjson.AppJSON
|
||||||
WarnToFailure bool
|
WarnToFailure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchCronEntries returns a list of cron commands for a given app
|
// FetchCronTasks returns a list of cron tasks for a given app
|
||||||
func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) {
|
func FetchCronTasks(input FetchCronTasksInput) ([]CronTask, error) {
|
||||||
appName := input.AppName
|
appName := input.AppName
|
||||||
commands := []TemplateCommand{}
|
tasks := []CronTask{}
|
||||||
isMaintenance := reportComputedMaintenance(appName) == "true"
|
isMaintenance := reportComputedMaintenance(appName) == "true"
|
||||||
|
|
||||||
if input.AppJSON == nil && input.AppName == "" {
|
if input.AppJSON == nil && input.AppName == "" {
|
||||||
@@ -86,29 +86,29 @@ func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) {
|
|||||||
if input.AppJSON == nil {
|
if input.AppJSON == nil {
|
||||||
appJSON, err := appjson.GetAppJSON(appName)
|
appJSON, err := appjson.GetAppJSON(appName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commands, fmt.Errorf("Unable to fetch app.json for app %s: %s", appName, err.Error())
|
return tasks, fmt.Errorf("Unable to fetch app.json for app %s: %s", appName, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
input.AppJSON = &appJSON
|
input.AppJSON = &appJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.AppJSON.Cron == nil {
|
if input.AppJSON.Cron == nil {
|
||||||
return commands, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range input.AppJSON.Cron {
|
for i, c := range input.AppJSON.Cron {
|
||||||
if c.Command == "" {
|
if c.Command == "" {
|
||||||
if input.WarnToFailure {
|
if input.WarnToFailure {
|
||||||
return commands, fmt.Errorf("Missing cron command for app %s (index %d)", appName, i)
|
return tasks, fmt.Errorf("Missing cron task command for app %s (index %d)", appName, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogWarn(fmt.Sprintf("Missing cron command for app %s (index %d)", appName, i))
|
common.LogWarn(fmt.Sprintf("Missing cron task command for app %s (index %d)", appName, i))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Schedule == "" {
|
if c.Schedule == "" {
|
||||||
if input.WarnToFailure {
|
if input.WarnToFailure {
|
||||||
return commands, fmt.Errorf("Missing cron schedule for app %s (index %d)", appName, i)
|
return tasks, fmt.Errorf("Missing cron schedule for app %s (index %d)", appName, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogWarn(fmt.Sprintf("Missing cron schedule for app %s (index %d)", appName, i))
|
common.LogWarn(fmt.Sprintf("Missing cron schedule for app %s (index %d)", appName, i))
|
||||||
@@ -118,10 +118,10 @@ func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) {
|
|||||||
parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor)
|
parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor)
|
||||||
_, err := parser.Parse(c.Schedule)
|
_, err := parser.Parse(c.Schedule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commands, fmt.Errorf("Invalid cron schedule for app %s (schedule %s): %s", appName, c.Schedule, err.Error())
|
return tasks, fmt.Errorf("Invalid cron schedule for app %s (schedule %s): %s", appName, c.Schedule, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
commands = append(commands, TemplateCommand{
|
tasks = append(tasks, CronTask{
|
||||||
App: appName,
|
App: appName,
|
||||||
Command: c.Command,
|
Command: c.Command,
|
||||||
Schedule: c.Schedule,
|
Schedule: c.Schedule,
|
||||||
@@ -130,14 +130,14 @@ func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchGlobalCronEntries returns a list of global cron commands
|
// FetchGlobalCronTasks returns a list of global cron tasks
|
||||||
// This function should only be used for the cron:list --global command
|
// This function should only be used for the cron:list --global command
|
||||||
// and not internally by the cron plugin
|
// and not internally by the cron plugin
|
||||||
func FetchGlobalCronEntries() ([]TemplateCommand, error) {
|
func FetchGlobalCronTasks() ([]CronTask, error) {
|
||||||
commands := []TemplateCommand{}
|
tasks := []CronTask{}
|
||||||
response, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
response, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||||
Trigger: "cron-entries",
|
Trigger: "cron-entries",
|
||||||
Args: []string{"docker-local"},
|
Args: []string{"docker-local"},
|
||||||
@@ -154,7 +154,7 @@ func FetchGlobalCronEntries() ([]TemplateCommand, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;")))
|
id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;")))
|
||||||
command := TemplateCommand{
|
task := CronTask{
|
||||||
ID: id,
|
ID: id,
|
||||||
Schedule: parts[0],
|
Schedule: parts[0],
|
||||||
Command: parts[1],
|
Command: parts[1],
|
||||||
@@ -163,14 +163,14 @@ func FetchGlobalCronEntries() ([]TemplateCommand, error) {
|
|||||||
Global: true,
|
Global: true,
|
||||||
}
|
}
|
||||||
if len(parts) == 3 {
|
if len(parts) == 3 {
|
||||||
command.LogFile = parts[2]
|
task.LogFile = parts[2]
|
||||||
}
|
}
|
||||||
commands = append(commands, command)
|
tasks = append(tasks, task)
|
||||||
}
|
}
|
||||||
return commands, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateCommandID creates a unique ID for a given app/command/schedule combination
|
// GenerateCommandID creates a unique ID for a given app/command/schedule combination
|
||||||
func GenerateCommandID(appName string, c appjson.CronCommand) string {
|
func GenerateCommandID(appName string, c appjson.CronTask) string {
|
||||||
return base36.EncodeToStringLc([]byte(appName + "===" + c.Command + "===" + c.Schedule))
|
return base36.EncodeToStringLc([]byte(appName + "===" + c.Command + "===" + c.Schedule))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func reportMailto(_ string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reportTasks(appName string) string {
|
func reportTasks(appName string) string {
|
||||||
c, _ := FetchCronEntries(FetchCronEntriesInput{AppName: appName})
|
c, _ := FetchCronTasks(FetchCronTasksInput{AppName: appName})
|
||||||
return strconv.Itoa(len(c))
|
return strconv.Itoa(len(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ func CommandList(appName string, format string) error {
|
|||||||
return fmt.Errorf("Invalid format specified, supported formats: json, stdout")
|
return fmt.Errorf("Invalid format specified, supported formats: json, stdout")
|
||||||
}
|
}
|
||||||
|
|
||||||
var entries []TemplateCommand
|
var tasks []CronTask
|
||||||
if appName == "--global" {
|
if appName == "--global" {
|
||||||
var err error
|
var err error
|
||||||
entries, err = FetchGlobalCronEntries()
|
tasks, err = FetchGlobalCronTasks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ func CommandList(appName string, format string) error {
|
|||||||
if err := common.VerifyAppName(appName); err != nil {
|
if err := common.VerifyAppName(appName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
entries, err = FetchCronEntries(FetchCronEntriesInput{AppName: appName})
|
tasks, err = FetchCronTasks(FetchCronTasksInput{AppName: appName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -42,8 +42,8 @@ func CommandList(appName string, format string) error {
|
|||||||
|
|
||||||
if format == "stdout" {
|
if format == "stdout" {
|
||||||
output := []string{"ID | Schedule | Maintenance | Command"}
|
output := []string{"ID | Schedule | Maintenance | Command"}
|
||||||
for _, entry := range entries {
|
for _, task := range tasks {
|
||||||
output = append(output, fmt.Sprintf("%s | %s | %t | %s", entry.ID, entry.Schedule, entry.Maintenance, entry.Command))
|
output = append(output, fmt.Sprintf("%s | %s | %t | %s", task.ID, task.Schedule, task.Maintenance, task.Command))
|
||||||
}
|
}
|
||||||
|
|
||||||
result := columnize.SimpleFormat(output)
|
result := columnize.SimpleFormat(output)
|
||||||
@@ -51,7 +51,7 @@ func CommandList(appName string, format string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := json.Marshal(entries)
|
out, err := json.Marshal(tasks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -82,13 +82,13 @@ func CommandReport(appName string, format string, infoFlag string) error {
|
|||||||
return ReportSingleApp(appName, format, infoFlag)
|
return ReportSingleApp(appName, format, infoFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommandRun executes a cron command on the fly
|
// CommandRun executes a cron task on the fly
|
||||||
func CommandRun(appName string, cronID string, detached bool) error {
|
func CommandRun(appName string, cronID string, detached bool) error {
|
||||||
if err := common.VerifyAppName(appName); err != nil {
|
if err := common.VerifyAppName(appName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := FetchCronEntries(FetchCronEntriesInput{AppName: appName})
|
tasks, err := FetchCronTasks(FetchCronTasksInput{AppName: appName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -98,9 +98,9 @@ func CommandRun(appName string, cronID string, detached bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
command := ""
|
command := ""
|
||||||
for _, entry := range entries {
|
for _, task := range tasks {
|
||||||
if entry.ID == cronID {
|
if task.ID == cronID {
|
||||||
command = entry.Command
|
command = task.Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ MAILTO={{ .Mailto }}
|
|||||||
PATH=/usr/local/bin:/usr/bin:/bin
|
PATH=/usr/local/bin:/usr/bin:/bin
|
||||||
SHELL=/bin/bash
|
SHELL=/bin/bash
|
||||||
|
|
||||||
{{ range $entry := .Commands -}}
|
{{ range $task := .Tasks -}}
|
||||||
{{ $entry.Schedule }} {{ $entry.CronCommand }}
|
{{ $task.Schedule }} {{ $task.DokkuRunCommand }}
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TriggerCronGetProperty(appName string, key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TriggerAppJSONIsValid validates the cron entries for a given app
|
// TriggerAppJSONIsValid validates the cron tasks for a given app
|
||||||
func TriggerAppJSONIsValid(appName string, appJSONPath string) error {
|
func TriggerAppJSONIsValid(appName string, appJSONPath string) error {
|
||||||
if !common.FileExists(appJSONPath) {
|
if !common.FileExists(appJSONPath) {
|
||||||
return nil
|
return nil
|
||||||
@@ -35,7 +35,7 @@ func TriggerAppJSONIsValid(appName string, appJSONPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = FetchCronEntries(FetchCronEntriesInput{
|
_, err = FetchCronTasks(FetchCronTasksInput{
|
||||||
AppName: appName,
|
AppName: appName,
|
||||||
AppJSON: &appJSON,
|
AppJSON: &appJSON,
|
||||||
WarnToFailure: true,
|
WarnToFailure: true,
|
||||||
|
|||||||
@@ -38,23 +38,23 @@ func deleteCrontab() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCronEntries() ([]cron.TemplateCommand, error) {
|
func generateCronTasks() ([]cron.CronTask, error) {
|
||||||
apps, _ := common.UnfilteredDokkuApps()
|
apps, _ := common.UnfilteredDokkuApps()
|
||||||
|
|
||||||
g := new(errgroup.Group)
|
g := new(errgroup.Group)
|
||||||
results := make(chan []cron.TemplateCommand, len(apps)+1)
|
results := make(chan []cron.CronTask, len(apps)+1)
|
||||||
for _, appName := range apps {
|
for _, appName := range apps {
|
||||||
appName := appName
|
appName := appName
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
scheduler := common.GetAppScheduler(appName)
|
scheduler := common.GetAppScheduler(appName)
|
||||||
if scheduler != "docker-local" {
|
if scheduler != "docker-local" {
|
||||||
results <- []cron.TemplateCommand{}
|
results <- []cron.CronTask{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := cron.FetchCronEntries(cron.FetchCronEntriesInput{AppName: appName})
|
c, err := cron.FetchCronTasks(cron.FetchCronTasksInput{AppName: appName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results <- []cron.TemplateCommand{}
|
results <- []cron.CronTask{}
|
||||||
common.LogWarn(err.Error())
|
common.LogWarn(err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -65,69 +65,69 @@ func generateCronEntries() ([]cron.TemplateCommand, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
commands := []cron.TemplateCommand{}
|
tasks := []cron.CronTask{}
|
||||||
response, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
response, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||||
Trigger: "cron-entries",
|
Trigger: "cron-entries",
|
||||||
Args: []string{"docker-local"},
|
Args: []string{"docker-local"},
|
||||||
})
|
})
|
||||||
for _, line := range strings.Split(response.StdoutContents(), "\n") {
|
for _, line := range strings.Split(response.StdoutContents(), "\n") {
|
||||||
if strings.TrimSpace(line) == "" {
|
if strings.TrimSpace(line) == "" {
|
||||||
results <- []cron.TemplateCommand{}
|
results <- []cron.CronTask{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(line, ";")
|
parts := strings.Split(line, ";")
|
||||||
if len(parts) != 2 && len(parts) != 3 {
|
if len(parts) != 2 && len(parts) != 3 {
|
||||||
results <- []cron.TemplateCommand{}
|
results <- []cron.CronTask{}
|
||||||
return fmt.Errorf("Invalid injected cron task: %v", line)
|
return fmt.Errorf("Invalid injected cron task: %v", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;")))
|
id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;")))
|
||||||
command := cron.TemplateCommand{
|
task := cron.CronTask{
|
||||||
ID: id,
|
ID: id,
|
||||||
Schedule: parts[0],
|
Schedule: parts[0],
|
||||||
AltCommand: parts[1],
|
AltCommand: parts[1],
|
||||||
Maintenance: false,
|
Maintenance: false,
|
||||||
}
|
}
|
||||||
if len(parts) == 3 {
|
if len(parts) == 3 {
|
||||||
command.LogFile = parts[2]
|
task.LogFile = parts[2]
|
||||||
}
|
}
|
||||||
commands = append(commands, command)
|
tasks = append(tasks, task)
|
||||||
}
|
}
|
||||||
results <- commands
|
results <- tasks
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
err := g.Wait()
|
err := g.Wait()
|
||||||
close(results)
|
close(results)
|
||||||
|
|
||||||
commands := []cron.TemplateCommand{}
|
tasks := []cron.CronTask{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commands, err
|
return tasks, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for result := range results {
|
for result := range results {
|
||||||
c := result
|
c := result
|
||||||
if len(c) > 0 && !c[0].Maintenance {
|
if len(c) > 0 && !c[0].Maintenance {
|
||||||
commands = append(commands, c...)
|
tasks = append(tasks, c...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCronEntries(scheduler string) error {
|
func writeCronTasks(scheduler string) error {
|
||||||
// allow empty scheduler, which means all apps (used by letsencrypt)
|
// allow empty scheduler, which means all apps (used by letsencrypt)
|
||||||
if scheduler != "docker-local" && scheduler != "" {
|
if scheduler != "docker-local" && scheduler != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commands, err := generateCronEntries()
|
tasks, err := generateCronTasks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(commands) == 0 {
|
if len(tasks) == 0 {
|
||||||
return deleteCrontab()
|
return deleteCrontab()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ func writeCronEntries(scheduler string) error {
|
|||||||
mailto := mailtoResults.StdoutContents()
|
mailto := mailtoResults.StdoutContents()
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"Commands": commands,
|
"Tasks": tasks,
|
||||||
"Mailfrom": mailfrom,
|
"Mailfrom": mailfrom,
|
||||||
"Mailto": mailto,
|
"Mailto": mailto,
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ func writeCronEntries(scheduler string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpFile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronEntries"))
|
tmpFile, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronTasks"))
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package schedulerdockerlocal
|
|||||||
|
|
||||||
// TriggerSchedulerCronWrite force updates the cron file for all apps
|
// TriggerSchedulerCronWrite force updates the cron file for all apps
|
||||||
func TriggerSchedulerCronWrite(scheduler string) error {
|
func TriggerSchedulerCronWrite(scheduler string) error {
|
||||||
return writeCronEntries(scheduler)
|
return writeCronTasks(scheduler)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,15 +312,15 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
|||||||
|
|
||||||
workingDir := common.GetWorkingDir(appName, image)
|
workingDir := common.GetWorkingDir(appName, image)
|
||||||
|
|
||||||
allCronEntries, err := cron.FetchCronEntries(cron.FetchCronEntriesInput{AppName: appName})
|
allCronTasks, err := cron.FetchCronTasks(cron.FetchCronTasksInput{AppName: appName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error fetching cron entries: %w", err)
|
return fmt.Errorf("Error fetching cron tasks: %w", err)
|
||||||
}
|
}
|
||||||
// remove maintenance cron entries
|
// remove maintenance cron tasks
|
||||||
cronEntries := []cron.TemplateCommand{}
|
cronTasks := []cron.CronTask{}
|
||||||
for _, cronEntry := range allCronEntries {
|
for _, cronTask := range allCronTasks {
|
||||||
if !cronEntry.Maintenance {
|
if !cronTask.Maintenance {
|
||||||
cronEntries = append(cronEntries, cronEntry)
|
cronTasks = append(cronTasks, cronTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,13 +605,13 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error listing cron jobs: %w", err)
|
return fmt.Errorf("Error listing cron jobs: %w", err)
|
||||||
}
|
}
|
||||||
for _, cronEntry := range cronEntries {
|
for _, cronTask := range cronTasks {
|
||||||
// todo: implement deployment annotations
|
// todo: implement deployment annotations
|
||||||
// todo: implement pod annotations
|
// todo: implement pod annotations
|
||||||
// todo: implement volumes
|
// todo: implement volumes
|
||||||
suffix := ""
|
suffix := ""
|
||||||
for _, cronJob := range cronJobs {
|
for _, cronJob := range cronJobs {
|
||||||
if cronJob.Labels["dokku.com/cron-id"] == cronEntry.ID {
|
if cronJob.Labels["dokku.com/cron-id"] == cronTask.ID {
|
||||||
var ok bool
|
var ok bool
|
||||||
suffix, ok = cronJob.Annotations["dokku.com/job-suffix"]
|
suffix, ok = cronJob.Annotations["dokku.com/job-suffix"]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -628,22 +628,22 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
|||||||
suffix = strings.ToLower(fmt.Sprintf("%X", b))
|
suffix = strings.ToLower(fmt.Sprintf("%X", b))
|
||||||
}
|
}
|
||||||
|
|
||||||
words, err := shellquote.Split(cronEntry.Command)
|
words, err := shellquote.Split(cronTask.Command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error parsing cron command: %w", err)
|
return fmt.Errorf("Error parsing cron task command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources, err := getProcessResources(appName, cronEntry.ID)
|
processResources, err := getProcessResources(appName, cronTask.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting process resources: %w", err)
|
return fmt.Errorf("Error getting process resources: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations, err := getAnnotations(appName, cronEntry.ID)
|
annotations, err := getAnnotations(appName, cronTask.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting process annotations: %w", err)
|
return fmt.Errorf("Error getting process annotations: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
labels, err := getLabels(appName, cronEntry.ID)
|
labels, err := getLabels(appName, cronTask.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting process labels: %w", err)
|
return fmt.Errorf("Error getting process labels: %w", err)
|
||||||
}
|
}
|
||||||
@@ -652,8 +652,8 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
|||||||
Args: words,
|
Args: words,
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
Cron: ProcessCron{
|
Cron: ProcessCron{
|
||||||
ID: cronEntry.ID,
|
ID: cronTask.ID,
|
||||||
Schedule: cronEntry.Schedule,
|
Schedule: cronTask.Schedule,
|
||||||
Suffix: suffix,
|
Suffix: suffix,
|
||||||
},
|
},
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
@@ -662,10 +662,10 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
|||||||
Resources: processResources,
|
Resources: processResources,
|
||||||
Volumes: processVolumes,
|
Volumes: processVolumes,
|
||||||
}
|
}
|
||||||
values.Processes[cronEntry.ID] = processValues
|
values.Processes[cronTask.ID] = processValues
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cronEntries) > 0 {
|
if len(cronTasks) > 0 {
|
||||||
b, err := templates.ReadFile("templates/chart/cron-job.yaml")
|
b, err := templates.ReadFile("templates/chart/cron-job.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error reading cron job template: %w", err)
|
return fmt.Errorf("Error reading cron job template: %w", err)
|
||||||
@@ -1443,14 +1443,14 @@ func TriggerSchedulerRunList(scheduler string, appName string, format string) er
|
|||||||
return fmt.Errorf("Error getting cron jobs: %w", err)
|
return fmt.Errorf("Error getting cron jobs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronJobEntry struct {
|
type CronJobTask struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
AppName string `json:"app"`
|
AppName string `json:"app"`
|
||||||
Command string `json:"command"`
|
Command string `json:"command"`
|
||||||
Schedule string `json:"schedule"`
|
Schedule string `json:"schedule"`
|
||||||
}
|
}
|
||||||
|
|
||||||
data := []CronJobEntry{}
|
data := []CronJobTask{}
|
||||||
lines := []string{"ID | Schedule | Command"}
|
lines := []string{"ID | Schedule | Command"}
|
||||||
for _, cronJob := range cronJobs {
|
for _, cronJob := range cronJobs {
|
||||||
command := ""
|
command := ""
|
||||||
@@ -1467,7 +1467,7 @@ func TriggerSchedulerRunList(scheduler string, appName string, format string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
lines = append(lines, fmt.Sprintf("%s | %s | %s", cronID, cronJob.Spec.Schedule, command))
|
lines = append(lines, fmt.Sprintf("%s | %s | %s", cronID, cronJob.Spec.Schedule, command))
|
||||||
data = append(data, CronJobEntry{
|
data = append(data, CronJobTask{
|
||||||
ID: cronID,
|
ID: cronID,
|
||||||
AppName: appName,
|
AppName: appName,
|
||||||
Command: command,
|
Command: command,
|
||||||
|
|||||||
Reference in New Issue
Block a user