Merge pull request #8937 from dokku/8912-cron-containers-are-never-retired-past-their-active-deadline
Retire cron containers past their active deadline
This commit is contained in:
@@ -42,6 +42,23 @@ var (
|
||||
|
||||
const MaintenancePropertyPrefix = "maintenance."
|
||||
|
||||
// DefaultTTLSeconds is how long a cron task may run before it is reaped. The
|
||||
// docker-local scheduler stamps this onto the container as the
|
||||
// com.dokku.active-deadline-seconds label, while the k3s scheduler renders it
|
||||
// as the CronJob's activeDeadlineSeconds.
|
||||
const DefaultTTLSeconds int64 = 86400
|
||||
|
||||
// validateTTLSeconds returns an error if the requested task lifetime is not a
|
||||
// positive number of seconds. A zero or negative deadline would either expire
|
||||
// the task the instant it starts or never expire it at all.
|
||||
func validateTTLSeconds(ttlSeconds int64) error {
|
||||
if ttlSeconds <= 0 {
|
||||
return fmt.Errorf("--ttl-seconds must be a positive integer")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CronTask is a struct that represents a cron task
|
||||
type CronTask struct {
|
||||
// ID is a unique identifier for the cron task
|
||||
|
||||
@@ -123,3 +123,32 @@ func TestDokkuRunCommandAppTaskIgnoresLogFile(t *testing.T) {
|
||||
t.Errorf("DokkuRunCommand() interpolated a redirect into an app task line: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateTTLSeconds pins that a cron task deadline must be a positive
|
||||
// number of seconds. A zero or negative value would either expire the task the
|
||||
// instant it starts or leave it running forever, and the docker-local retire
|
||||
// pass only reaps containers whose deadline has actually elapsed.
|
||||
func TestValidateTTLSeconds(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
ttlSeconds int64
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "default", ttlSeconds: DefaultTTLSeconds},
|
||||
{name: "positive override", ttlSeconds: 1},
|
||||
{name: "zero", ttlSeconds: 0, wantErr: true},
|
||||
{name: "negative", ttlSeconds: -1, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateTTLSeconds(tc.ttlSeconds)
|
||||
if tc.wantErr && err == nil {
|
||||
t.Errorf("validateTTLSeconds(%d) = nil, want an error", tc.ttlSeconds)
|
||||
}
|
||||
if !tc.wantErr && err != nil {
|
||||
t.Errorf("validateTTLSeconds(%d) = %v, want nil", tc.ttlSeconds, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ Additional commands:`
|
||||
cron:list <app> [--format json|stdout], List scheduled cron tasks for an app
|
||||
cron:report [<app>] [<flag>], Display report about an app
|
||||
cron:resume <app> <cron_id>, Resume a cron task
|
||||
cron:run <app> <cron_id> [--detach], Run a cron task on the fly
|
||||
cron:run <app> <cron_id> [--detach] [--ttl-seconds SECONDS], Run a cron task on the fly
|
||||
cron:set [--global|<app>] <key> <value>, Set or clear a cron property for an app
|
||||
cron:suspend <app> <cron_id>, Suspend a cron task`
|
||||
)
|
||||
|
||||
@@ -49,10 +49,11 @@ func main() {
|
||||
case "run":
|
||||
args := flag.NewFlagSet("cron:run", flag.ExitOnError)
|
||||
detached := args.Bool("detach", false, "--detach: run the container in a detached mode")
|
||||
ttlSeconds := args.Int64("ttl-seconds", cron.DefaultTTLSeconds, "--ttl-seconds: number of seconds the task may run before it is reaped")
|
||||
args.Parse(os.Args[2:])
|
||||
appName := args.Arg(0)
|
||||
cronID := args.Arg(1)
|
||||
err = cron.CommandRun(appName, cronID, *detached)
|
||||
err = cron.CommandRun(appName, cronID, *detached, *ttlSeconds)
|
||||
case "set":
|
||||
args := flag.NewFlagSet("cron:set", flag.ExitOnError)
|
||||
global := args.Bool("global", false, "--global: set a global property")
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
@@ -97,11 +98,15 @@ func CommandResume(appName string, cronID string) error {
|
||||
}
|
||||
|
||||
// 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, ttlSeconds int64) error {
|
||||
if err := common.VerifyAppName(appName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateTTLSeconds(ttlSeconds); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tasks, err := FetchCronTasks(FetchCronTasksInput{AppName: appName})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -139,7 +144,7 @@ func CommandRun(appName string, cronID string, detached bool) error {
|
||||
os.Setenv("DOKKU_CONCURRENCY_POLICY", concurrencyPolicy)
|
||||
os.Setenv("DOKKU_CRON_ID", cronID)
|
||||
os.Setenv("DOKKU_RM_CONTAINER", "1")
|
||||
os.Setenv("DOKKU_RUN_TTL_SECONDS", "86400")
|
||||
os.Setenv("DOKKU_RUN_TTL_SECONDS", strconv.FormatInt(ttlSeconds, 10))
|
||||
scheduler := common.GetAppScheduler(appName)
|
||||
args := append([]string{scheduler, appName, "0", "--"}, fields...)
|
||||
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||
|
||||
Reference in New Issue
Block a user