From 82f3613ed7f802ebff68a0adece35e94f688e488 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 8 Jun 2025 01:29:12 -0400 Subject: [PATCH 01/97] chore: update docker-container-healthchecker from 0.11.5 to 0.14.0 --- contrib/dependencies.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/dependencies.json b/contrib/dependencies.json index 2038615f8..7c9e08ec3 100644 --- a/contrib/dependencies.json +++ b/contrib/dependencies.json @@ -2,10 +2,10 @@ "dependencies": [ { "name": "docker-container-healthchecker", - "version": "0.11.5", + "version": "0.14.0", "urls": { - "amd64": "https://github.com/dokku/docker-container-healthchecker/releases/download/v0.11.5/docker-container-healthchecker-linux-amd64", - "arm64": "https://github.com/dokku/docker-container-healthchecker/releases/download/v0.11.5/docker-container-healthchecker-linux-arm64" + "amd64": "https://github.com/dokku/docker-container-healthchecker/releases/download/v0.14.0/docker-container-healthchecker-linux-amd64", + "arm64": "https://github.com/dokku/docker-container-healthchecker/releases/download/v0.14.0/docker-container-healthchecker-linux-arm64" } }, { From 10b31fcd4a21aab60649392ff865481a5549a9ee Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 8 Jun 2025 01:30:29 -0400 Subject: [PATCH 02/97] chore: update herokuish from 0.10.3 to 0.11.0 --- contrib/dependencies.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/dependencies.json b/contrib/dependencies.json index 2038615f8..8ebac685d 100644 --- a/contrib/dependencies.json +++ b/contrib/dependencies.json @@ -86,11 +86,11 @@ }, { "name": "herokuish", - "version": "0.10.3", + "version": "0.11.0", "urls": { - "amd64": "https://github.com/gliderlabs/herokuish/releases/download/v0.10.3/herokuish_0.10.3_linux_x86_64.tgz", - "arm64": "https://github.com/gliderlabs/herokuish/releases/download/v0.10.3/herokuish_0.10.3_linux_x86_64.tgz" + "amd64": "https://github.com/gliderlabs/herokuish/releases/download/v0.11.0/herokuish_0.11.0_linux_x86_64.tgz", + "arm64": "https://github.com/gliderlabs/herokuish/releases/download/v0.11.0/herokuish_0.11.0_linux_x86_64.tgz" } } ] -} +} \ No newline at end of file From 1060189d78cfccf85187e02dfb4750ffa9c9726c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 22:18:12 +0000 Subject: [PATCH 03/97] chore(deps): bump express in /tests/apps/nodejs-express-noappjson Bumps [express](https://github.com/expressjs/express) from 4.21.2 to 5.1.0. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.2...v5.1.0) --- updated-dependencies: - dependency-name: express dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- tests/apps/nodejs-express-noappjson/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/apps/nodejs-express-noappjson/package.json b/tests/apps/nodejs-express-noappjson/package.json index 444351953..80c575069 100644 --- a/tests/apps/nodejs-express-noappjson/package.json +++ b/tests/apps/nodejs-express-noappjson/package.json @@ -2,7 +2,7 @@ "name": "node-example", "version": "0.0.1", "dependencies": { - "express": "4.21.x" + "express": "5.1.x" }, "engines": { "node": "4.2.x" From b44ccae7969c79b1ad913d41bd45afe4ec9aee75 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 8 Jun 2025 19:27:52 -0400 Subject: [PATCH 04/97] chore: bump nodejs from 4.2.x to 18.x.x --- tests/apps/nodejs-express-noappjson/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/apps/nodejs-express-noappjson/package.json b/tests/apps/nodejs-express-noappjson/package.json index 80c575069..d6ab60955 100644 --- a/tests/apps/nodejs-express-noappjson/package.json +++ b/tests/apps/nodejs-express-noappjson/package.json @@ -5,6 +5,6 @@ "express": "5.1.x" }, "engines": { - "node": "4.2.x" + "node": "18.x.x" } } From 98e7f467b779e9ff86e578b5c53603d6ba8e3ed3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 00:01:31 +0000 Subject: [PATCH 05/97] chore(deps): bump express from 4.21.2 to 5.1.0 in /tests/apps/nodejs-express Dependabot couldn't find the original pull request head commit, f30b38c741b6433fafe368d09d886d93b81a5830. --- tests/apps/nodejs-express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/apps/nodejs-express/package.json b/tests/apps/nodejs-express/package.json index 444351953..80c575069 100644 --- a/tests/apps/nodejs-express/package.json +++ b/tests/apps/nodejs-express/package.json @@ -2,7 +2,7 @@ "name": "node-example", "version": "0.0.1", "dependencies": { - "express": "4.21.x" + "express": "5.1.x" }, "engines": { "node": "4.2.x" From 80f35370abec786baf03f57464e9b20fa8416204 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 8 Jun 2025 20:07:43 -0400 Subject: [PATCH 06/97] chore: bump nodejs from 4.2.x to 18.x.x --- tests/apps/nodejs-express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/apps/nodejs-express/package.json b/tests/apps/nodejs-express/package.json index 80c575069..d6ab60955 100644 --- a/tests/apps/nodejs-express/package.json +++ b/tests/apps/nodejs-express/package.json @@ -5,6 +5,6 @@ "express": "5.1.x" }, "engines": { - "node": "4.2.x" + "node": "18.x.x" } } From c0eb12f572312ac74286dae1c66a27292955480b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:56:49 -0400 Subject: [PATCH 07/97] feat: add ability to set maintenance mode for all cron tasks running for an app Closes #6953 --- docs/processes/scheduled-cron-tasks.md | 34 +++++++++++++------------- plugins/cron/Makefile | 2 +- plugins/cron/cron.go | 14 ++++++++--- plugins/cron/report.go | 26 +++++++++++++++++--- plugins/cron/src/triggers/triggers.go | 5 ++++ plugins/cron/triggers.go | 17 ++++++++++--- 6 files changed, 70 insertions(+), 28 deletions(-) diff --git a/docs/processes/scheduled-cron-tasks.md b/docs/processes/scheduled-cron-tasks.md index 145c890c6..4994d507c 100644 --- a/docs/processes/scheduled-cron-tasks.md +++ b/docs/processes/scheduled-cron-tasks.md @@ -53,39 +53,39 @@ When running scheduled cron tasks, there are a few items to be aware of: - 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 MAILFROM value -> [!IMPORTANT] -> New as of 0.35.14 +### Changing cron management settings -Users can specify a value for `MAILFROM` via the global `mailfrom` cron property by using the `cron:set` command. +The `cron` plugin provides a number of settings that can be used to managed deployments on a per-app basis. The following table outlines ones not covered elsewhere: + +| Name | Description | Level | Global Default | +|-----------------------|----------------------------------------------------------------|-------------|----------------| +| `mailfrom` | Sets the `MAILFROM` variable in a cron file for cron reporting | Global-only | empty string | +| `maintenance` | Whether to have cron running for the app or not. | App-only | `false` | +| `mailto` | Sets the `MAILTO` variable in a cron file for cron reporting | Global-only | empty string | + +All settings can be set via the `ps:set` command. Using `maintenance` as an example: ```shell -dokku cron:set --global mailfrom example@example.com +dokku ps:set node-js-app maintenance true ``` -All output for individual cron runs will be sent from the specified email. - -Cron emails can be reset to use the system email by running the `cron:set` command for the global `mailfrom` property with no value. +The default value may be set by passing an empty value for the option in question: ```shell -dokku cron:set --global mailfrom +dokku ps:set node-js-app maintenance ``` -#### 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. +If a property can be set globally - such as `mailto`, use the `--global` flag. If not set for an app, the global value will apply if it exists. ```shell -dokku cron:set --global mailto example@example.com +dokku ps:set --global maintenance true ``` -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. +The global default value may be set by passing an empty value for the option. ```shell -dokku cron:set --global mailto +dokku ps:set --global maintenance ``` #### Listing Cron tasks diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index e0e1dbe33..920941136 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/report subcommands/run subcommands/set -TRIGGERS = triggers/cron-get-property +TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = cron diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index 056eadb4c..61a86bfce 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -12,14 +12,16 @@ import ( var ( // DefaultProperties is a map of all valid cron properties with corresponding default property values DefaultProperties = map[string]string{ - "mailfrom": "", - "mailto": "", + "mailfrom": "", + "mailto": "", + "maintenance": "false", } // GlobalProperties is a map of all valid global cron properties GlobalProperties = map[string]bool{ - "mailfrom": true, - "mailto": true, + "mailfrom": true, + "mailto": true, + "maintenance": true, } ) @@ -59,6 +61,10 @@ func (t TemplateCommand) CronCommand() string { // FetchCronEntries returns a list of cron commands for a given app func FetchCronEntries(appName string) ([]TemplateCommand, error) { commands := []TemplateCommand{} + if reportComputedMaintenance(appName) == "true" { + return commands, nil + } + appJSON, err := appjson.GetAppJSON(appName) if err != nil { return commands, fmt.Errorf("Unable to fetch app.json for app %s: %s", appName, err.Error()) diff --git a/plugins/cron/report.go b/plugins/cron/report.go index c5fe8dc5d..769ed59a6 100644 --- a/plugins/cron/report.go +++ b/plugins/cron/report.go @@ -13,9 +13,12 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } flags := map[string]common.ReportFunc{ - "--cron-mailfrom": reportMailfrom, - "--cron-mailto": reportMailto, - "--cron-task-count": reportTasks, + "--cron-mailfrom": reportMailfrom, + "--cron-mailto": reportMailto, + "--cron-task-count": reportTasks, + "--cron-global-maintenance": reportGlobalMaintenance, + "--cron-computed-maintenance": reportComputedMaintenance, + "--cron-maintenance": reportMaintenance, } flagKeys := []string{} @@ -41,3 +44,20 @@ func reportTasks(appName string) string { c, _ := FetchCronEntries(appName) return strconv.Itoa(len(c)) } + +func reportGlobalMaintenance(_ string) string { + return common.PropertyGet("cron", "--global", "maintenance") +} + +func reportComputedMaintenance(appName string) string { + maintenance := common.PropertyGet("cron", appName, "maintenance") + if maintenance == "true" { + return "true" + } + + return common.PropertyGetDefault("cron", "--global", "maintenance", DefaultProperties["maintenance"]) +} + +func reportMaintenance(appName string) string { + return common.PropertyGet("cron", appName, "maintenance") +} diff --git a/plugins/cron/src/triggers/triggers.go b/plugins/cron/src/triggers/triggers.go index 083cbb25b..6e0de5e7a 100644 --- a/plugins/cron/src/triggers/triggers.go +++ b/plugins/cron/src/triggers/triggers.go @@ -43,6 +43,11 @@ func main() { case "report": appName := flag.Arg(0) err = cron.ReportSingleApp(appName, "", "") + case "scheduler-stop": + scheduler := flag.Arg(0) + appName := flag.Arg(1) + removeContainers := flag.Arg(2) + err = cron.TriggerSchedulerStop(scheduler, appName, removeContainers) default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) } diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go index 42f99044a..321e2b8a9 100644 --- a/plugins/cron/triggers.go +++ b/plugins/cron/triggers.go @@ -10,14 +10,16 @@ import ( // TriggerCronGetProperty writes the cron key to stdout for a given app container func TriggerCronGetProperty(appName string, key string) error { validProperties := map[string]bool{ - "mailfrom": true, - "mailto": true, + "mailfrom": true, + "mailto": true, + "maintenance": true, } if !validProperties[key] { return errors.New("Invalid cron property specified") } - fmt.Println(common.PropertyGet("cron", appName, key)) + value := common.PropertyGetDefault("cron", appName, key, DefaultProperties[key]) + fmt.Println(value) return nil } @@ -57,3 +59,12 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { func TriggerPostDelete(appName string) error { return common.PropertyDestroy("cron", appName) } + +// TriggerSchedulerStop stops the scheduler for a given app container +func TriggerSchedulerStop(scheduler string, appName string, removeContainers string) error { + if scheduler != "docker-local" { + return nil + } + + return nil +} From 0f4b5d43050d6e03914021fc5a24401bf8b305ff Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:03:43 -0400 Subject: [PATCH 08/97] fix: continue including maintenance mode cron tasks in the cron:list subcommand --- plugins/cron/cron.go | 16 +++++++++------- plugins/cron/subcommands.go | 4 ++-- plugins/scheduler-docker-local/functions.go | 9 +++++---- plugins/scheduler-k3s/triggers.go | 9 ++++++++- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index 61a86bfce..dd30c25e8 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -44,6 +44,9 @@ type TemplateCommand struct { // LogFile is the log file to write to LogFile string `json:"-"` + + // Maintenance is whether the cron command is in maintenance mode + Maintenance bool `json:"maintenance"` } // CronCommand returns the command to run for a given cron command @@ -61,9 +64,7 @@ func (t TemplateCommand) CronCommand() string { // FetchCronEntries returns a list of cron commands for a given app func FetchCronEntries(appName string) ([]TemplateCommand, error) { commands := []TemplateCommand{} - if reportComputedMaintenance(appName) == "true" { - return commands, nil - } + isMaintenance := reportComputedMaintenance(appName) == "true" appJSON, err := appjson.GetAppJSON(appName) if err != nil { @@ -82,10 +83,11 @@ func FetchCronEntries(appName string) ([]TemplateCommand, error) { } commands = append(commands, TemplateCommand{ - App: appName, - Command: c.Command, - Schedule: c.Schedule, - ID: GenerateCommandID(appName, c), + App: appName, + Command: c.Command, + Schedule: c.Schedule, + ID: GenerateCommandID(appName, c), + Maintenance: isMaintenance, }) } diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index 593f6212a..4e0e56462 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -32,9 +32,9 @@ func CommandList(appName string, format string) error { } if format == "stdout" { - output := []string{"ID | Schedule | Command"} + output := []string{"ID | Schedule | Maintenance | Command"} for _, entry := range entries { - output = append(output, fmt.Sprintf("%s | %s | %s", entry.ID, entry.Schedule, entry.Command)) + output = append(output, fmt.Sprintf("%s | %s | %t | %s", entry.ID, entry.Schedule, entry.Maintenance, entry.Command)) } result := columnize.SimpleFormat(output) diff --git a/plugins/scheduler-docker-local/functions.go b/plugins/scheduler-docker-local/functions.go index bcf852426..e6ec71eea 100644 --- a/plugins/scheduler-docker-local/functions.go +++ b/plugins/scheduler-docker-local/functions.go @@ -83,9 +83,10 @@ func generateCronEntries() ([]cron.TemplateCommand, error) { id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;"))) command := cron.TemplateCommand{ - ID: id, - Schedule: parts[0], - AltCommand: parts[1], + ID: id, + Schedule: parts[0], + AltCommand: parts[1], + Maintenance: false, } if len(parts) == 3 { command.LogFile = parts[2] @@ -106,7 +107,7 @@ func generateCronEntries() ([]cron.TemplateCommand, error) { for result := range results { c := result - if len(c) > 0 { + if len(c) > 0 && !c[0].Maintenance { commands = append(commands, c...) } } diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index cb4c27e49..5fa5e8b62 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -266,10 +266,17 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e workingDir := common.GetWorkingDir(appName, image) - cronEntries, err := cron.FetchCronEntries(appName) + allCronEntries, err := cron.FetchCronEntries(appName) if err != nil { return fmt.Errorf("Error fetching cron entries: %w", err) } + // remove maintenance cron entries + cronEntries := []cron.TemplateCommand{} + for _, cronEntry := range allCronEntries { + if !cronEntry.Maintenance { + cronEntries = append(cronEntries, cronEntry) + } + } domains := []string{} if _, ok := processes["web"]; ok { From d0b87936db50126d8ffbf853859c8e96659769e6 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 8 Jun 2025 22:35:17 -0400 Subject: [PATCH 09/97] docs: fix typo --- docs/processes/scheduled-cron-tasks.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/processes/scheduled-cron-tasks.md b/docs/processes/scheduled-cron-tasks.md index 4994d507c..ac1ed4809 100644 --- a/docs/processes/scheduled-cron-tasks.md +++ b/docs/processes/scheduled-cron-tasks.md @@ -64,28 +64,28 @@ The `cron` plugin provides a number of settings that can be used to managed depl | `maintenance` | Whether to have cron running for the app or not. | App-only | `false` | | `mailto` | Sets the `MAILTO` variable in a cron file for cron reporting | Global-only | empty string | -All settings can be set via the `ps:set` command. Using `maintenance` as an example: +All settings can be set via the `cron:set` command. Using `maintenance` as an example: ```shell -dokku ps:set node-js-app maintenance true +dokku cron:set node-js-app maintenance true ``` The default value may be set by passing an empty value for the option in question: ```shell -dokku ps:set node-js-app maintenance +dokku cron:set node-js-app maintenance ``` If a property can be set globally - such as `mailto`, use the `--global` flag. If not set for an app, the global value will apply if it exists. ```shell -dokku ps:set --global maintenance true +dokku cron:set --global maintenance true ``` The global default value may be set by passing an empty value for the option. ```shell -dokku ps:set --global maintenance +dokku cron:set --global maintenance ``` #### Listing Cron tasks From 758d9f8a832a3c6a29f305cb3e08d7437c6d14d9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:22:24 -0400 Subject: [PATCH 10/97] feat: add config-unset trigger --- plugins/config/Makefile | 2 +- plugins/config/src/triggers/triggers.go | 12 +++++++++++- plugins/config/triggers.go | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/plugins/config/Makefile b/plugins/config/Makefile index 86ffcb36b..0cf396bd8 100644 --- a/plugins/config/Makefile +++ b/plugins/config/Makefile @@ -1,6 +1,6 @@ GOARCH ?= amd64 SUBCOMMANDS = subcommands/bundle subcommands/clear subcommands/export subcommands/get subcommands/keys subcommands/show subcommands/set subcommands/unset -TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global triggers/post-app-clone-setup triggers/post-app-rename-setup +TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global triggers/config-unset triggers/post-app-clone-setup triggers/post-app-rename-setup BUILD = commands config_sub subcommands triggers PLUGIN_NAME = config diff --git a/plugins/config/src/triggers/triggers.go b/plugins/config/src/triggers/triggers.go index 1f8f58732..96e95d455 100644 --- a/plugins/config/src/triggers/triggers.go +++ b/plugins/config/src/triggers/triggers.go @@ -24,7 +24,7 @@ func main() { global := flag.Arg(1) merged := flag.Arg(2) format := flag.Arg(3) - config.TriggerConfigExport(appName, global, merged, format) + err = config.TriggerConfigExport(appName, global, merged, format) case "config-get": appName := flag.Arg(0) key := flag.Arg(1) @@ -36,6 +36,16 @@ func main() { case "config-get-global": key := flag.Arg(0) err = config.TriggerConfigGetGlobal(key) + case "config-unset": + appName := flag.Arg(0) + key := flag.Arg(1) + restart := flag.Arg(2) + if *global { + appName = "--global" + key = flag.Arg(0) + restart = flag.Arg(1) + } + err = config.TriggerConfigUnset(appName, key, common.ToBool(restart)) case "post-app-clone-setup": oldAppName := flag.Arg(0) newAppName := flag.Arg(1) diff --git a/plugins/config/triggers.go b/plugins/config/triggers.go index af96d114c..b0c3a57f3 100644 --- a/plugins/config/triggers.go +++ b/plugins/config/triggers.go @@ -45,6 +45,12 @@ func TriggerConfigGetGlobal(key string) error { return nil } +// TriggerConfigUnset unsets an app config value by key +func TriggerConfigUnset(appName string, key string, restart bool) error { + UnsetMany(appName, []string{key}, restart) + return nil +} + // TriggerPostAppCloneSetup creates new buildpacks files func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { oldEnv, err := LoadAppEnv(oldAppName) From 9beab4d50de9fbe6febadb53a9da59f1dcdef89a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:26:47 -0400 Subject: [PATCH 11/97] refactor: migrate DOKKU_DOCKER_STOP_TIMEOUT to ps setting --- docs/appendices/0.36.0-migration-guide.md | 6 +++ docs/configuration/environment-variables.md | 1 - docs/deployment/zero-downtime-deploys.md | 5 ++- docs/processes/process-management.md | 33 ++++++++++++++++ plugins/ports/triggers.go | 18 +++++++-- plugins/ps/ps.go | 8 ++-- plugins/ps/report.go | 38 ++++++++++++++----- plugins/ps/triggers.go | 37 +++++++++++++++++- .../scheduler-docker-local/internal-functions | 2 +- .../scheduler-docker-local/scheduler-deploy | 2 +- .../scheduler-docker-local/scheduler-run-stop | 2 +- 11 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 docs/appendices/0.36.0-migration-guide.md diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md new file mode 100644 index 000000000..2a79b6668 --- /dev/null +++ b/docs/appendices/0.36.0-migration-guide.md @@ -0,0 +1,6 @@ +# 0.35.0 Migration Guide + +## Changes + +- Process stop timeouts are now configured via the `ps` property `stop-timeout-seconds`. Existing `DOKKU_DOCKER_STOP_TIMEOUT` environment variables will be automatically migrated to the new value. +- Processes now default to a `30` second stop timeout. \ No newline at end of file diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index 3734d44c6..047571c22 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -112,7 +112,6 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_DISABLE_PROXY` | none | `dokku proxy:disable`
`dokku proxy:enable` | Disables the proxy in front of your application, resulting in publicly routing the docker container. | | `DOKKU_DISABLE_ANSI_PREFIX_REMOVAL` | none | `dokku config:set`
`/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | Disables removal of the ANSI prefix during deploys. Can be used in cases where the client deployer does not understand ansi escape codes. | | `DOKKU_DISABLE_APP_AUTOCREATION` | none | `dokku config:set` | Disables automatic creation of a non-existent app on deploy. | -| `DOKKU_DOCKER_STOP_TIMEOUT` | `10` | `dokku config:set` | Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands _also_ respect this value. If not specified, the docker defaults for the [docker stop command](https://docs.docker.com/engine/reference/commandline/stop/) will be used.| | `DOKKU_DOCKERFILE_CACHE_BUILD` | none | `dokku config:set` | | | `DOKKU_DOCKERFILE_START_CMD` | none | `dokku config:set` | | | `DOKKU_PARALLEL_ARGUMENTS`. | none | `dokku config:set` | Allows passing custom arguments to parallel for `ps:*all` commands | diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index b811b2d1e..91bbf765e 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -36,12 +36,15 @@ dokku checks:set node-js-app wait-to-retire 30 Defaults to `60`. +## Setting stop grace period + +You can set the `stop-timeout` property on the `ps` plugin to change this value (default: `30`). See the [process management documentation](/docs/processes/process-management.md#changing-process-management-settings) for more information. + ## Configuring check settings using the `config` plugin There are certain settings that can be configured via environment variables: - `DOKKU_DEFAULT_CHECKS_WAIT`: (default: `10`) If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. -- `DOKKU_DOCKER_STOP_TIMEOUT`: (default: `10`) Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands *also* respect this value. If not specified, the Docker defaults for the [`docker stop` command](https://docs.docker.com/engine/reference/commandline/stop/) will be used. The following settings may also be specified in the `app.json` file, though are available as environment variables in order to ease application reuse. diff --git a/docs/processes/process-management.md b/docs/processes/process-management.md index abc64ebac..c94796b87 100644 --- a/docs/processes/process-management.md +++ b/docs/processes/process-management.md @@ -107,6 +107,39 @@ proctype: qty web: 1 ``` +### Changing process management settings + +The `ps` plugin provides a number of settings that can be used to managed deployments on a per-app basis. The following table outlines ones not covered elsewhere: + +| Name | Description | Global Default | +|-----------------------|---------------------------------------------------|--------------------| +| `stop-timeout` | Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands *also* respect this value. If not specified, the Docker defaults for the [`docker stop` command](https://docs.docker.com/engine/reference/commandline/stop/) will be used. | `30` | + +All settings can be set via the `scheduler-k3s:set` command. Using `stop-timeout` as an example: + +```shell +dokku scheduler-k3s:set node-js-app stop-timeout 60 +``` + +The default value may be set by passing an empty value for the option in question: + +```shell +dokku scheduler-k3s:set node-js-app stop-timeout +``` + +Properties can also be set globally. If not set for an app, the global value will apply. + +```shell +dokku scheduler-k3s:set --global stop-timeout 60 +``` + +The global default value may be set by passing an empty value for the option. + +```shell +dokku scheduler-k3s:set --global stop-timeout +``` + + ### Defining Processes #### Procfile diff --git a/plugins/ports/triggers.go b/plugins/ports/triggers.go index 4391170f1..bb4e7a83d 100644 --- a/plugins/ports/triggers.go +++ b/plugins/ports/triggers.go @@ -25,8 +25,16 @@ func TriggerInstall() error { continue } - common.LogInfo1(fmt.Sprintf("Migrating DOKKU_PROXY_PORT_MAP to ports map property for %s", appName)) - portMapString := config.GetWithDefault(appName, "DOKKU_PROXY_PORT_MAP", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, + }) + portMapString := results.StdoutContents() + if portMapString == "" { + continue + } + + common.LogVerboseQuiet(fmt.Sprintf("Setting %s ports property 'map' to %v", appName, portMapString)) portMaps, _ := parsePortMapString(portMapString) propertyValue := []string{} @@ -38,7 +46,11 @@ func TriggerInstall() error { return err } - if err := config.UnsetMany(appName, []string{"DOKKU_PROXY_PORT_MAP"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_PROXY_PORT_MAP"}, + }) + if err != nil { return err } } diff --git a/plugins/ps/ps.go b/plugins/ps/ps.go index 7e1e2f4d9..307306f24 100644 --- a/plugins/ps/ps.go +++ b/plugins/ps/ps.go @@ -13,13 +13,15 @@ const RunInSerial = 0 var ( // DefaultProperties is a map of all valid ps properties with corresponding default property values DefaultProperties = map[string]string{ - "restart-policy": "on-failure:10", - "procfile-path": "", + "restart-policy": "on-failure:10", + "procfile-path": "", + "stop-timeout-seconds": "30", } // GlobalProperties is a map of all valid global ps properties GlobalProperties = map[string]bool{ - "procfile-path": true, + "procfile-path": true, + "stop-timeout-seconds": true, } ) diff --git a/plugins/ps/report.go b/plugins/ps/report.go index 5e0ddfe40..74c4c17a9 100644 --- a/plugins/ps/report.go +++ b/plugins/ps/report.go @@ -15,15 +15,18 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { } flags := map[string]common.ReportFunc{ - "--deployed": reportDeployed, - "--processes": reportProcesses, - "--ps-can-scale": reportCanScale, - "--ps-restart-policy": reportRestartPolicy, - "--ps-computed-procfile-path": reportComputedProcfilePath, - "--ps-global-procfile-path": reportGlobalProcfilePath, - "--ps-procfile-path": reportProcfilePath, - "--restore": reportRestore, - "--running": reportRunningState, + "--deployed": reportDeployed, + "--processes": reportProcesses, + "--ps-can-scale": reportCanScale, + "--ps-restart-policy": reportRestartPolicy, + "--ps-computed-procfile-path": reportComputedProcfilePath, + "--ps-global-procfile-path": reportGlobalProcfilePath, + "--ps-procfile-path": reportProcfilePath, + "--restore": reportRestore, + "--running": reportRunningState, + "--global-stop-timeout-seconds": reportGlobalStopTimeoutSeconds, + "--computed-stop-timeout-seconds": reportComputedStopTimeoutSeconds, + "--stop-timeout-seconds": reportStopTimeoutSeconds, } extraFlags := addStatusFlags(appName, infoFlag) @@ -146,3 +149,20 @@ func reportRestore(appName string) string { func reportRunningState(appName string) string { return getRunningState(appName) } + +func reportComputedStopTimeoutSeconds(appName string) string { + value := reportStopTimeoutSeconds(appName) + if value == "" { + value = reportGlobalStopTimeoutSeconds(appName) + } + + return value +} + +func reportGlobalStopTimeoutSeconds(appName string) string { + return common.PropertyGetDefault("ps", "--global", "stop-timeout-seconds", "30") +} + +func reportStopTimeoutSeconds(appName string) string { + return common.PropertyGetDefault("ps", appName, "stop-timeout-seconds", "30") +} diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 674369db5..90a4e001b 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -164,8 +164,29 @@ func TriggerInstall() error { if common.FileExists(dokkuScaleExtracted) { os.Remove(dokkuScaleExtracted) } - } + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, + }) + stopTimeout := results.StdoutContents() + if stopTimeout == "" { + continue + } + + common.LogVerboseQuiet(fmt.Sprintf("Setting %s ps property 'stop-timeout-seconds' to %v", appName, stopTimeout)) + if err := common.PropertyWrite("ps", appName, "stop-timeout-seconds", stopTimeout); err != nil { + return err + } + + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_DOCKER_STOP_TIMEOUT"}, + }) + if err != nil { + return err + } + } return nil } @@ -320,3 +341,17 @@ func TriggerPsCurrentScale(appName string) error { func TriggerPsSetScale(appName string, skipDeploy bool, clearExisting bool, processTuples []string) error { return scaleSet(appName, skipDeploy, clearExisting, processTuples) } + +func TriggerPsGetProperty(appName string, property string) error { + computedValueMap := map[string]common.ReportFunc{ + "stop-timeout-seconds": reportComputedStopTimeoutSeconds, + } + + fn, ok := computedValueMap[property] + if !ok { + return fmt.Errorf("Invalid network property specified: %v", property) + } + + fmt.Println(fn(appName)) + return nil +} diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index ebce46b95..b0c32a137 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -114,7 +114,7 @@ fn-scheduler-docker-local-retire-container() { return fi - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" if [[ "$STATE" == "restarting" ]]; then diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index 3192c0e63..b222fef8f 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -32,7 +32,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { local oldids=$(get_app_container_ids "$APP" "$PROCESS_TYPE") DOKKU_NETWORK_BIND_ALL="$(plugn trigger network-get-property "$APP" bind-all-interfaces)" - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" DOKKU_START_CMD="$(config_get "$APP" DOKKU_START_CMD || true)" diff --git a/plugins/scheduler-docker-local/scheduler-run-stop b/plugins/scheduler-docker-local/scheduler-run-stop index d46f9ad72..85b124fdd 100755 --- a/plugins/scheduler-docker-local/scheduler-run-stop +++ b/plugins/scheduler-docker-local/scheduler-run-stop @@ -51,7 +51,7 @@ trigger-scheduler-docker-local-scheduler-run-stop() { verify_app_name "$APP" - DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" if [[ -n "$CONTAINER_NAME" ]]; then fn-scheduler-docker-local-stop-container "$CONTAINER_NAME" "$DOKKU_DOCKER_STOP_TIMEOUT" return $? From 4942ebaec167e40df229ebf0cb942d0497cdc8b0 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:27:58 -0400 Subject: [PATCH 12/97] refactor: remove use of config plugin from scheduler plugin --- plugins/scheduler/go.mod | 2 -- plugins/scheduler/go.sum | 2 -- plugins/scheduler/triggers.go | 27 +++++++++++++++++++++------ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/plugins/scheduler/go.mod b/plugins/scheduler/go.mod index 938db56c5..c5742cd6b 100644 --- a/plugins/scheduler/go.mod +++ b/plugins/scheduler/go.mod @@ -4,7 +4,6 @@ go 1.23.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 github.com/spf13/pflag v1.0.6 ) @@ -13,7 +12,6 @@ require ( github.com/fatih/color v1.18.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/joho/godotenv v1.2.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/plugins/scheduler/go.sum b/plugins/scheduler/go.sum index 12803b5db..b61585913 100644 --- a/plugins/scheduler/go.sum +++ b/plugins/scheduler/go.sum @@ -10,8 +10,6 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc= -github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= diff --git a/plugins/scheduler/triggers.go b/plugins/scheduler/triggers.go index 3d350e2db..a83ce8013 100644 --- a/plugins/scheduler/triggers.go +++ b/plugins/scheduler/triggers.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/config" ) // TriggerSchedulerDetect outputs a manually selected scheduler for the app @@ -36,30 +35,46 @@ func TriggerInstall() error { return nil } - globalScheduler := config.GetWithDefault("--global", "DOKKU_SCHEDULER", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get-global", + Args: []string{"DOKKU_SCHEDULER"}, + }) + globalScheduler := results.StdoutContents() if globalScheduler != "" { common.LogVerboseQuiet(fmt.Sprintf("Setting scheduler property 'selected' to %v", globalScheduler)) if err := common.PropertyWrite("scheduler", "--global", "selected", globalScheduler); err != nil { return err } - if err := config.UnsetMany("--global", []string{"DOKKU_SCHEDULER"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{"--global", "DOKKU_SCHEDULER"}, + }) + if err != nil { common.LogWarn(err.Error()) } } for _, appName := range apps { - scheduler := config.GetWithDefault(appName, "DOKKU_SCHEDULER", "") + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-get", + Args: []string{appName, "DOKKU_SCHEDULER"}, + }) + scheduler := results.StdoutContents() if scheduler == "" { continue } - common.LogVerboseQuiet(fmt.Sprintf("Setting scheduler property 'selected' to %v", scheduler)) + common.LogVerboseQuiet(fmt.Sprintf("Setting %s scheduler property 'selected' to %v", appName, scheduler)) if err := common.PropertyWrite("scheduler", appName, "selected", scheduler); err != nil { return err } - if err := config.UnsetMany(appName, []string{"DOKKU_SCHEDULER"}, false); err != nil { + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "config-unset", + Args: []string{appName, "DOKKU_SCHEDULER"}, + }) + if err != nil { common.LogWarn(err.Error()) } } From f0bf7aa0b63e5550592b193f2864d0448d0b952b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:28:26 -0400 Subject: [PATCH 13/97] fix: also migrate one more usage --- plugins/scheduler-docker-local/scheduler-stop | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 88dd43091..0977c1eb7 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -8,13 +8,14 @@ trigger-scheduler-docker-local-scheduler-stop() { declare desc="scheduler-docker-local scheduler-stop plugin trigger" declare trigger="scheduler-stop" declare DOKKU_SCHEDULER="$1" APP="$2" REMOVE_CONTAINERS="$3" + local DOKKU_DOCKER_STOP_TIMEOUT DOKKU_APP_RUNNING_CONTAINER_IDS if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then return fi - local DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) - local DOKKU_DOCKER_STOP_TIMEOUT="$(config_get "$APP" DOKKU_DOCKER_STOP_TIMEOUT || true)" + DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" [[ -n "$DOKKU_DOCKER_STOP_TIMEOUT" ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" From 5d1a3fb0791d28c5c5bfcdfbf6e8aa995be6e59e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:45:45 -0400 Subject: [PATCH 14/97] fix: add missing ps-get-property --- plugins/ps/Makefile | 2 +- plugins/ps/src/triggers/triggers.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/ps/Makefile b/plugins/ps/Makefile index 8e8457fc4..3f8016f33 100644 --- a/plugins/ps/Makefile +++ b/plugins/ps/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/inspect subcommands/rebuild subcommands/report subcommands/restart subcommands/restore subcommands/retire subcommands/scale subcommands/set subcommands/start subcommands/stop -TRIGGERS = triggers/app-restart triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-release-builder triggers/post-stop triggers/procfile-get-command triggers/procfile-exists triggers/ps-can-scale triggers/ps-current-scale triggers/ps-set-scale triggers/report +TRIGGERS = triggers/app-restart triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone triggers/post-app-clone-setup triggers/post-app-rename triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/post-release-builder triggers/post-stop triggers/procfile-get-command triggers/procfile-exists triggers/ps-can-scale triggers/ps-current-scale triggers/ps-get-property triggers/ps-set-scale triggers/report BUILD = commands subcommands triggers PLUGIN_NAME = ps diff --git a/plugins/ps/src/triggers/triggers.go b/plugins/ps/src/triggers/triggers.go index 4d2821dad..3ec4beaec 100644 --- a/plugins/ps/src/triggers/triggers.go +++ b/plugins/ps/src/triggers/triggers.go @@ -75,8 +75,11 @@ func main() { case "ps-current-scale": appName := flag.Arg(0) err = ps.TriggerPsCurrentScale(appName) - case "ps-set-scale": + case "ps-get-property": appName := flag.Arg(0) + property := flag.Arg(1) + err = ps.TriggerPsGetProperty(appName, property) + case "ps-set-scale": appName, args := common.ShiftString(flag.Args()) skipDeploy, args := common.ShiftString(args) clearExisting, processTuples := common.ShiftString(args) From 3a16724fbe4e491a5fe80316c6af1d07e25aef5e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 04:47:05 -0400 Subject: [PATCH 15/97] docs: correct commands --- docs/processes/process-management.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/processes/process-management.md b/docs/processes/process-management.md index c94796b87..86b80982e 100644 --- a/docs/processes/process-management.md +++ b/docs/processes/process-management.md @@ -115,31 +115,30 @@ The `ps` plugin provides a number of settings that can be used to managed deploy |-----------------------|---------------------------------------------------|--------------------| | `stop-timeout` | Configurable grace period given to the `docker stop` command. If a container has not stopped by this time, a `kill -9` signal or equivalent is sent in order to force-terminate the container. Both the `ps:stop` and `apps:destroy` commands *also* respect this value. If not specified, the Docker defaults for the [`docker stop` command](https://docs.docker.com/engine/reference/commandline/stop/) will be used. | `30` | -All settings can be set via the `scheduler-k3s:set` command. Using `stop-timeout` as an example: +All settings can be set via the `ps:set` command. Using `stop-timeout` as an example: ```shell -dokku scheduler-k3s:set node-js-app stop-timeout 60 +dokku ps:set node-js-app stop-timeout 60 ``` The default value may be set by passing an empty value for the option in question: ```shell -dokku scheduler-k3s:set node-js-app stop-timeout +dokku ps:set node-js-app stop-timeout ``` Properties can also be set globally. If not set for an app, the global value will apply. ```shell -dokku scheduler-k3s:set --global stop-timeout 60 +dokku ps:set --global stop-timeout 60 ``` The global default value may be set by passing an empty value for the option. ```shell -dokku scheduler-k3s:set --global stop-timeout +dokku ps:set --global stop-timeout ``` - ### Defining Processes #### Procfile From 86a6b2e24633d419e4d1ecd9d88d9f0c843401e4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:09:49 -0400 Subject: [PATCH 16/97] fix: reference correct variable --- docs/deployment/zero-downtime-deploys.md | 2 +- plugins/scheduler-docker-local/internal-functions | 2 +- plugins/scheduler-docker-local/scheduler-deploy | 2 +- plugins/scheduler-docker-local/scheduler-run-stop | 2 +- plugins/scheduler-docker-local/scheduler-stop | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/deployment/zero-downtime-deploys.md b/docs/deployment/zero-downtime-deploys.md index 91bbf765e..e769b2193 100644 --- a/docs/deployment/zero-downtime-deploys.md +++ b/docs/deployment/zero-downtime-deploys.md @@ -38,7 +38,7 @@ Defaults to `60`. ## Setting stop grace period -You can set the `stop-timeout` property on the `ps` plugin to change this value (default: `30`). See the [process management documentation](/docs/processes/process-management.md#changing-process-management-settings) for more information. +You can set the `stop-timeout-seconds` property on the `ps` plugin to change this value (default: `30`). See the [process management documentation](/docs/processes/process-management.md#changing-process-management-settings) for more information. ## Configuring check settings using the `config` plugin diff --git a/plugins/scheduler-docker-local/internal-functions b/plugins/scheduler-docker-local/internal-functions index b0c32a137..d0cfdb061 100755 --- a/plugins/scheduler-docker-local/internal-functions +++ b/plugins/scheduler-docker-local/internal-functions @@ -114,7 +114,7 @@ fn-scheduler-docker-local-retire-container() { return fi - DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" if [[ "$STATE" == "restarting" ]]; then diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index b222fef8f..e7e9f4adf 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -32,7 +32,7 @@ trigger-scheduler-docker-local-scheduler-deploy() { local oldids=$(get_app_container_ids "$APP" "$PROCESS_TYPE") DOKKU_NETWORK_BIND_ALL="$(plugn trigger network-get-property "$APP" bind-all-interfaces)" - DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds)" [[ $DOKKU_DOCKER_STOP_TIMEOUT ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" DOKKU_START_CMD="$(config_get "$APP" DOKKU_START_CMD || true)" diff --git a/plugins/scheduler-docker-local/scheduler-run-stop b/plugins/scheduler-docker-local/scheduler-run-stop index 85b124fdd..d72015f38 100755 --- a/plugins/scheduler-docker-local/scheduler-run-stop +++ b/plugins/scheduler-docker-local/scheduler-run-stop @@ -51,7 +51,7 @@ trigger-scheduler-docker-local-scheduler-run-stop() { verify_app_name "$APP" - DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" if [[ -n "$CONTAINER_NAME" ]]; then fn-scheduler-docker-local-stop-container "$CONTAINER_NAME" "$DOKKU_DOCKER_STOP_TIMEOUT" return $? diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 0977c1eb7..6e3f4b8f3 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -15,7 +15,7 @@ trigger-scheduler-docker-local-scheduler-stop() { fi DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) - DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout || true)" + DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" [[ -n "$DOKKU_DOCKER_STOP_TIMEOUT" ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" From 5e1f177d1c5c7f5edc7e3cc685ed0197f8ebe8c3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 06:04:18 -0400 Subject: [PATCH 17/97] chore: revert local --- plugins/scheduler-docker-local/scheduler-stop | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 6e3f4b8f3..8e336e95b 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -8,14 +8,13 @@ trigger-scheduler-docker-local-scheduler-stop() { declare desc="scheduler-docker-local scheduler-stop plugin trigger" declare trigger="scheduler-stop" declare DOKKU_SCHEDULER="$1" APP="$2" REMOVE_CONTAINERS="$3" - local DOKKU_DOCKER_STOP_TIMEOUT DOKKU_APP_RUNNING_CONTAINER_IDS if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then return fi - DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) - DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" + local DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids "$APP" 2>/dev/null) + local DOKKU_DOCKER_STOP_TIMEOUT="$(plugn trigger ps-get-property "$APP" stop-timeout-seconds || true)" [[ -n "$DOKKU_DOCKER_STOP_TIMEOUT" ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" From 7b0d3b14bcf6a7b5ec7364394281634822476ccc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:12:31 -0400 Subject: [PATCH 18/97] fix: stop all app containers when calling ps:stop Closes #6872 --- plugins/scheduler-docker-local/scheduler-stop | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index 8e336e95b..aaea1299e 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -4,6 +4,12 @@ set -eo pipefail source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/config/functions" +fn-stop-container() { + declare APP="$1" CID="$2" + "$DOCKER_BIN" container update --restart=no "$CID" &>/dev/null || true + "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG "$CID" &>/dev/null || true +} + trigger-scheduler-docker-local-scheduler-stop() { declare desc="scheduler-docker-local scheduler-stop plugin trigger" declare trigger="scheduler-stop" @@ -19,14 +25,22 @@ trigger-scheduler-docker-local-scheduler-stop() { [[ -n "$DOKKU_DOCKER_STOP_TIMEOUT" ]] && DOCKER_STOP_TIME_ARG="--time=${DOKKU_DOCKER_STOP_TIMEOUT}" if [[ -n "$DOKKU_APP_RUNNING_CONTAINER_IDS" ]]; then - # Disable the container restart policy - "$DOCKER_BIN" container update --restart=no $DOKKU_APP_RUNNING_CONTAINER_IDS &>/dev/null || true + for CID in $DOKKU_APP_RUNNING_CONTAINER_IDS; do + fn-stop-container "$APP" "$CID" + done + fi - "$DOCKER_BIN" container stop $DOCKER_STOP_TIME_ARG $DOKKU_APP_RUNNING_CONTAINER_IDS >/dev/null || true + # Stop all other containers related to the app + # fetch all containers with the com.dokku.app-name label + local DOKKU_APP_CIDS="$(docker container ls -q -f label=com.dokku.app-name="$APP")" + if [[ -n "$DOKKU_APP_CIDS" ]]; then + for CID in $DOKKU_APP_CIDS; do + fn-stop-container "$APP" "$CID" + done fi if [[ "$REMOVE_CONTAINERS" == "true" ]]; then - local DOKKU_APP_CIDS=$(get_app_container_ids "$APP") + local DOKKU_APP_CIDS="$(docker ps -q -f label=com.dokku.app-name="$APP")" if [[ -n "$DOKKU_APP_CIDS" ]]; then for CID in $DOKKU_APP_CIDS; do From 2dea993804e364329856543063d2bb1239d9952b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:23:02 -0400 Subject: [PATCH 19/97] fix: also stop k8s cron tasks when calling ps:stop --- plugins/scheduler-k3s/k8s.go | 29 +++++++++++++++++++++++++++++ plugins/scheduler-k3s/triggers.go | 9 +++++++++ 2 files changed, 38 insertions(+) diff --git a/plugins/scheduler-k3s/k8s.go b/plugins/scheduler-k3s/k8s.go index 2cb7a8095..494d5ab9a 100644 --- a/plugins/scheduler-k3s/k8s.go +++ b/plugins/scheduler-k3s/k8s.go @@ -940,3 +940,32 @@ func streamLogsFromRequest(ctx context.Context, request rest.ResponseWrapper, ou } } } + +// SuspendCronJobsInput contains all the information needed to suspend a Kubernetes cron job +type SuspendCronJobsInput struct { + // LabelSelector is the Kubernetes label selector + LabelSelector string + + // Namespace is the Kubernetes namespace + Namespace string +} + +// SuspendCronJobs suspends a Kubernetes cron job +func (k KubernetesClient) SuspendCronJobs(ctx context.Context, input SuspendCronJobsInput) error { + cronJobs, err := k.Client.BatchV1().CronJobs(input.Namespace).List(ctx, metav1.ListOptions{ + LabelSelector: input.LabelSelector, + }) + if err != nil { + return err + } + + for _, cronJob := range cronJobs.Items { + cronJob.Spec.Suspend = ptr.To(true) + _, err := k.Client.BatchV1().CronJobs(input.Namespace).Update(ctx, &cronJob, metav1.UpdateOptions{}) + if err != nil { + return err + } + } + + return nil +} diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 5fa5e8b62..22fec8b5a 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -1499,5 +1499,14 @@ func TriggerSchedulerStop(scheduler string, appName string) error { } } + // get all cronjobs for the app + err = clientset.SuspendCronJobs(ctx, SuspendCronJobsInput{ + Namespace: namespace, + LabelSelector: fmt.Sprintf("app.kubernetes.io/part-of=%s", appName), + }) + if err != nil { + return fmt.Errorf("Error suspending cron jobs: %w", err) + } + return nil } From 398a78b2f6d92b0fb54f4c7bc63812f617fd12c9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:39:55 -0400 Subject: [PATCH 20/97] refactor: move cron-write to scheduler-cron-write --- .../schedulers/scheduler-management.md | 6 ++-- docs/development/plugin-triggers.md | 36 ++++++++++--------- plugins/cron/subcommands.go | 4 ++- plugins/scheduler-docker-local/Makefile | 2 +- plugins/scheduler-docker-local/post-delete | 2 +- plugins/scheduler-docker-local/post-deploy | 2 +- .../src/triggers/triggers.go | 2 +- tests/unit/cron.bats | 8 ++--- 8 files changed, 35 insertions(+), 27 deletions(-) diff --git a/docs/deployment/schedulers/scheduler-management.md b/docs/deployment/schedulers/scheduler-management.md index 0189092bf..4fac3ce0f 100644 --- a/docs/deployment/schedulers/scheduler-management.md +++ b/docs/deployment/schedulers/scheduler-management.md @@ -98,7 +98,7 @@ At this time, the following dokku commands are used to implement a complete sche - `apps:clone`: handles app cloning - triggers: post-app-clone-setup - `cron`: generates cron tasks for the app - - triggers: cron-write + - triggers: scheduler-cron-write - `deploy`: deploys app proceses and checks the status of a deploy - triggers: scheduler-app-status, scheduler-deploy, scheduler-is-deployed, scheduler-logs-failed - `enter`: enters a running container @@ -112,7 +112,9 @@ At this time, the following dokku commands are used to implement a complete sche - `run:logs`: fetches running container logs - triggers: scheduler-run-logs - `ps:stop`: stops app processes - - triggers: scheduler-stop + - triggers: scheduler-stop, scheduler-cron-write +- `ps:start`: starts app processes + - triggers: scheduler-deploy, scheduler-cron-write - `ps:inspect`: outputs inspect output for processes in an app - triggers: scheduler-inspect diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index a8551d54e..e37e22c90 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -583,22 +583,6 @@ 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. @@ -2445,6 +2429,26 @@ DOKKU_SCHEDULER="$1"; APP="$2"; # TODO ``` +### `scheduler-cron-write` + +> [!WARNING] +> The scheduler plugin trigger apis are under development and may change +> between minor releases until the 1.0 release. + +- Description: Force triggers writing out cron entries. Arguments are optional. +- Invoked by: `ps:start`, `ps:stop`, `cron:set` +- Arguments: `$DOKKU_SCHEDULER $APP` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +# TODO +``` + ### `scheduler-deploy` > [!WARNING] diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index 4e0e56462..aede7d39b 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -130,8 +130,10 @@ func CommandSet(appName string, property string, value string) error { } common.CommandPropertySet("cron", appName, property, value, DefaultProperties, GlobalProperties) + scheduler := common.GetAppScheduler(appName) _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "cron-write", + Trigger: "scheduler-cron-write", + Args: []string{scheduler, appName}, StreamStdio: true, }) return err diff --git a/plugins/scheduler-docker-local/Makefile b/plugins/scheduler-docker-local/Makefile index 09f04eabb..bd7c75ae1 100644 --- a/plugins/scheduler-docker-local/Makefile +++ b/plugins/scheduler-docker-local/Makefile @@ -1,4 +1,4 @@ -TRIGGERS = triggers/cron-write +TRIGGERS = triggers/scheduler-cron-write BUILD = triggers PLUGIN_NAME = scheduler-docker-local diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index 472643544..703343f15 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -15,7 +15,7 @@ trigger-scheduler-docker-local-post-delete() { # remove all application containers "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true - plugn trigger cron-write + plugn trigger scheduler-cron-write } trigger-scheduler-docker-local-post-delete "$@" diff --git a/plugins/scheduler-docker-local/post-deploy b/plugins/scheduler-docker-local/post-deploy index 3c19037f1..9541385ce 100755 --- a/plugins/scheduler-docker-local/post-deploy +++ b/plugins/scheduler-docker-local/post-deploy @@ -7,7 +7,7 @@ trigger-scheduler-docker-local-post-deploy() { declare trigger="post-deploy" declare APP="$1" - plugn trigger cron-write + plugn trigger scheduler-cron-write } trigger-scheduler-docker-local-post-deploy "$@" diff --git a/plugins/scheduler-docker-local/src/triggers/triggers.go b/plugins/scheduler-docker-local/src/triggers/triggers.go index 86b030d9d..d3ce7aa11 100644 --- a/plugins/scheduler-docker-local/src/triggers/triggers.go +++ b/plugins/scheduler-docker-local/src/triggers/triggers.go @@ -18,7 +18,7 @@ func main() { var err error switch trigger { - case "cron-write": + case "scheduler-cron-write": err = schedulerdockerlocal.TriggerCronWrite() default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) diff --git a/tests/unit/cron.bats b/tests/unit/cron.bats index 097104af9..e22145aa7 100644 --- a/tests/unit/cron.bats +++ b/tests/unit/cron.bats @@ -121,7 +121,7 @@ teardown() { 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" + run /bin/bash -c "dokku plugin:trigger scheduler-cron-write" echo "output: $output" echo "status: $status" assert_success @@ -136,7 +136,7 @@ teardown() { 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" + run /bin/bash -c "dokku plugin:trigger scheduler-cron-write" echo "output: $output" echo "status: $status" assert_success @@ -151,7 +151,7 @@ teardown() { 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" + run /bin/bash -c "dokku plugin:trigger scheduler-cron-write" echo "output: $output" echo "status: $status" assert_success @@ -166,7 +166,7 @@ teardown() { 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" + run /bin/bash -c "dokku plugin:trigger scheduler-cron-write" echo "output: $output" echo "status: $status" assert_success From b663f71878d388a595f175623b4cebf16248a7fe Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 05:51:27 -0400 Subject: [PATCH 21/97] feat: only write cron entries when working on docker-local scheduler --- plugins/scheduler-docker-local/functions.go | 7 ++++++- plugins/scheduler-docker-local/post-delete | 3 ++- plugins/scheduler-docker-local/post-deploy | 3 ++- plugins/scheduler-docker-local/scheduler-stop | 2 ++ plugins/scheduler-docker-local/src/triggers/triggers.go | 3 ++- plugins/scheduler-docker-local/triggers.go | 4 ++-- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/scheduler-docker-local/functions.go b/plugins/scheduler-docker-local/functions.go index e6ec71eea..adf576314 100644 --- a/plugins/scheduler-docker-local/functions.go +++ b/plugins/scheduler-docker-local/functions.go @@ -115,7 +115,12 @@ func generateCronEntries() ([]cron.TemplateCommand, error) { return commands, nil } -func writeCronEntries() error { +func writeCronEntries(scheduler string) error { + // allow empty scheduler, which means all apps (used by letsencrypt) + if scheduler != "docker-local" && scheduler != "" { + return nil + } + commands, err := generateCronEntries() if err != nil { return err diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index 703343f15..cd8480f80 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -15,7 +15,8 @@ trigger-scheduler-docker-local-post-delete() { # remove all application containers "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true - plugn trigger scheduler-cron-write + local DOKKU_SCHEDULER="$(get_app_scheduler "$APP")" + plugn trigger scheduler-cron-write "$DOKKU_SCHEDULER" "$APP" } trigger-scheduler-docker-local-post-delete "$@" diff --git a/plugins/scheduler-docker-local/post-deploy b/plugins/scheduler-docker-local/post-deploy index 9541385ce..325b1bd52 100755 --- a/plugins/scheduler-docker-local/post-deploy +++ b/plugins/scheduler-docker-local/post-deploy @@ -7,7 +7,8 @@ trigger-scheduler-docker-local-post-deploy() { declare trigger="post-deploy" declare APP="$1" - plugn trigger scheduler-cron-write + local DOKKU_SCHEDULER="$(get_app_scheduler "$APP")" + plugn trigger scheduler-cron-write "$DOKKU_SCHEDULER" "$APP" } trigger-scheduler-docker-local-post-deploy "$@" diff --git a/plugins/scheduler-docker-local/scheduler-stop b/plugins/scheduler-docker-local/scheduler-stop index aaea1299e..12fcb5400 100755 --- a/plugins/scheduler-docker-local/scheduler-stop +++ b/plugins/scheduler-docker-local/scheduler-stop @@ -50,6 +50,8 @@ trigger-scheduler-docker-local-scheduler-stop() { "$DOCKER_BIN" container rm --force $DOKKU_APP_CIDS &>/dev/null || true fi fi + + plugn trigger scheduler-cron-write "$DOKKU_SCHEDULER" "$APP" } trigger-scheduler-docker-local-scheduler-stop "$@" diff --git a/plugins/scheduler-docker-local/src/triggers/triggers.go b/plugins/scheduler-docker-local/src/triggers/triggers.go index d3ce7aa11..4d11721ca 100644 --- a/plugins/scheduler-docker-local/src/triggers/triggers.go +++ b/plugins/scheduler-docker-local/src/triggers/triggers.go @@ -19,7 +19,8 @@ func main() { var err error switch trigger { case "scheduler-cron-write": - err = schedulerdockerlocal.TriggerCronWrite() + scheduler := flag.Arg(0) + err = schedulerdockerlocal.TriggerCronWrite(scheduler) default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) } diff --git a/plugins/scheduler-docker-local/triggers.go b/plugins/scheduler-docker-local/triggers.go index 018ff411e..785fc9f31 100644 --- a/plugins/scheduler-docker-local/triggers.go +++ b/plugins/scheduler-docker-local/triggers.go @@ -1,6 +1,6 @@ package schedulerdockerlocal // TriggerCronWrite force updates the cron file for all apps -func TriggerCronWrite() error { - return writeCronEntries() +func TriggerCronWrite(scheduler string) error { + return writeCronEntries(scheduler) } From 36da77a2ff47a62eca913b774f0fb4af6c4e8b60 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 9 Mar 2025 06:33:16 -0400 Subject: [PATCH 22/97] fix: write cron in pre-delete phase --- plugins/cron/Makefile | 2 +- plugins/cron/src/triggers/triggers.go | 3 +++ plugins/cron/triggers.go | 17 ++++++++++++++++- plugins/scheduler-docker-local/post-delete | 3 --- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index 920941136..20ddf156e 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/report subcommands/run subcommands/set -TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/scheduler-stop +TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/pre-delete triggers/post-delete triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = cron diff --git a/plugins/cron/src/triggers/triggers.go b/plugins/cron/src/triggers/triggers.go index 6e0de5e7a..a5a5ed98d 100644 --- a/plugins/cron/src/triggers/triggers.go +++ b/plugins/cron/src/triggers/triggers.go @@ -40,6 +40,9 @@ func main() { case "post-delete": appName := flag.Arg(0) err = cron.TriggerPostDelete(appName) + case "pre-delete": + appName := flag.Arg(0) + err = cron.TriggerPreDelete(appName) case "report": appName := flag.Arg(0) err = cron.ReportSingleApp(appName, "", "") diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go index 321e2b8a9..3b5c5d929 100644 --- a/plugins/cron/triggers.go +++ b/plugins/cron/triggers.go @@ -55,9 +55,24 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { return nil } +// TriggerPreDelete stops cron for a given app +func TriggerPreDelete(appName string) error { + scheduler := common.GetAppScheduler(appName) + _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "scheduler-cron-write", + Args: []string{scheduler, appName}, + StreamStdio: true, + }) + return err +} + // TriggerPostDelete destroys the cron property for a given app container func TriggerPostDelete(appName string) error { - return common.PropertyDestroy("cron", appName) + if err := common.PropertyDestroy("cron", appName); err != nil { + return err + } + + return nil } // TriggerSchedulerStop stops the scheduler for a given app container diff --git a/plugins/scheduler-docker-local/post-delete b/plugins/scheduler-docker-local/post-delete index cd8480f80..109b07748 100755 --- a/plugins/scheduler-docker-local/post-delete +++ b/plugins/scheduler-docker-local/post-delete @@ -14,9 +14,6 @@ trigger-scheduler-docker-local-post-delete() { # remove all application containers "$DOCKER_BIN" container ls --filter "label=com.dokku.app-name=${APP}" -q | xargs -n1 -I {} "$DOCKER_BIN" container rm --force {} &>/dev/null || true - - local DOKKU_SCHEDULER="$(get_app_scheduler "$APP")" - plugn trigger scheduler-cron-write "$DOKKU_SCHEDULER" "$APP" } trigger-scheduler-docker-local-post-delete "$@" From 2758b3cc47846ae19fa932adf73e96f879ad7e1d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 10 Jun 2025 23:36:10 -0400 Subject: [PATCH 23/97] chore: rename trigger function --- plugins/scheduler-docker-local/src/triggers/triggers.go | 2 +- plugins/scheduler-docker-local/triggers.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scheduler-docker-local/src/triggers/triggers.go b/plugins/scheduler-docker-local/src/triggers/triggers.go index 4d11721ca..7ee657f92 100644 --- a/plugins/scheduler-docker-local/src/triggers/triggers.go +++ b/plugins/scheduler-docker-local/src/triggers/triggers.go @@ -20,7 +20,7 @@ func main() { switch trigger { case "scheduler-cron-write": scheduler := flag.Arg(0) - err = schedulerdockerlocal.TriggerCronWrite(scheduler) + err = schedulerdockerlocal.TriggerSchedulerCronWrite(scheduler) default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) } diff --git a/plugins/scheduler-docker-local/triggers.go b/plugins/scheduler-docker-local/triggers.go index 785fc9f31..4f9fded0d 100644 --- a/plugins/scheduler-docker-local/triggers.go +++ b/plugins/scheduler-docker-local/triggers.go @@ -1,6 +1,6 @@ package schedulerdockerlocal -// TriggerCronWrite force updates the cron file for all apps -func TriggerCronWrite(scheduler string) error { +// TriggerSchedulerCronWrite force updates the cron file for all apps +func TriggerSchedulerCronWrite(scheduler string) error { return writeCronEntries(scheduler) } From 67003ed469a5157c3d0a53f8419d5016c9089b50 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 10 Jun 2025 23:39:35 -0400 Subject: [PATCH 24/97] fix: skip cron entries that are missing a command or a schedule --- plugins/cron/cron.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index dd30c25e8..667f5ecf7 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -4,6 +4,7 @@ import ( "fmt" appjson "github.com/dokku/dokku/plugins/app-json" + "github.com/dokku/dokku/plugins/common" "github.com/multiformats/go-base36" cronparser "github.com/robfig/cron/v3" @@ -75,7 +76,17 @@ func FetchCronEntries(appName string) ([]TemplateCommand, error) { return commands, nil } - for _, c := range appJSON.Cron { + for i, c := range appJSON.Cron { + if c.Command == "" { + common.LogWarn(fmt.Sprintf("Missing cron command for app %s (index %d)", appName, i)) + continue + } + + if c.Schedule == "" { + common.LogWarn(fmt.Sprintf("Missing cron schedule for app %s (index %d)", appName, i)) + continue + } + parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor) _, err := parser.Parse(c.Schedule) if err != nil { From 73be6989ff1deff46af6fa5b7411cdef426a8ba2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 10 Jun 2025 23:55:06 -0400 Subject: [PATCH 25/97] fix: ensure invalid cron triggers fail a deploy --- plugins/app-json/triggers.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index 35d88240c..92a896e4e 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -1,6 +1,7 @@ package appjson import ( + "encoding/json" "fmt" "os" "path" @@ -140,6 +141,35 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { } } + if hasAppJSON(appName) { + b, err := os.ReadFile(processSpecificAppJSON) + if err != nil { + return err + } + + content := strings.TrimSpace(string(b)) + if content == "" { + return nil + } + + var appJSON AppJSON + if err := json.Unmarshal([]byte(content), &appJSON); err != nil { + return fmt.Errorf("Unable to unmarshal app.json: %v", err.Error()) + } + + if appJSON.Cron != nil { + for i, c := range appJSON.Cron { + if c.Command == "" { + return fmt.Errorf("Missing cron command for app %s (index %d)", appName, i) + } + + if c.Schedule == "" { + return fmt.Errorf("Missing cron schedule for app %s (index %d)", appName, i) + } + } + } + } + // TODO: add validation to app.json file by ensuring it can be deserialized return nil } From 6f0039ebf15b7ebd84b509188846c5c647fed5fc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 18 Jun 2025 23:51:39 -0400 Subject: [PATCH 26/97] fix: add missing source --- plugins/scheduler-docker-local/post-deploy | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scheduler-docker-local/post-deploy b/plugins/scheduler-docker-local/post-deploy index 325b1bd52..0b56c6d24 100755 --- a/plugins/scheduler-docker-local/post-deploy +++ b/plugins/scheduler-docker-local/post-deploy @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -eo pipefail +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" [[ $DOKKU_TRACE ]] && set -x trigger-scheduler-docker-local-post-deploy() { From 776013b0f49c3495279bd7dc36144b1461a36e31 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 01:09:15 -0400 Subject: [PATCH 27/97] fix: write the cron config after the app.json has been deleted Without this, we try to write the config for the app even though it doesn't exist anymore, causing issues with deleting apps with invalid cron configs. --- plugins/cron/Makefile | 2 +- plugins/cron/src/triggers/triggers.go | 3 --- plugins/cron/triggers.go | 11 +++++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index 20ddf156e..920941136 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/report subcommands/run subcommands/set -TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/pre-delete triggers/post-delete triggers/scheduler-stop +TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = cron diff --git a/plugins/cron/src/triggers/triggers.go b/plugins/cron/src/triggers/triggers.go index a5a5ed98d..6e0de5e7a 100644 --- a/plugins/cron/src/triggers/triggers.go +++ b/plugins/cron/src/triggers/triggers.go @@ -40,9 +40,6 @@ func main() { case "post-delete": appName := flag.Arg(0) err = cron.TriggerPostDelete(appName) - case "pre-delete": - appName := flag.Arg(0) - err = cron.TriggerPreDelete(appName) case "report": appName := flag.Arg(0) err = cron.ReportSingleApp(appName, "", "") diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go index 3b5c5d929..383a7cd3a 100644 --- a/plugins/cron/triggers.go +++ b/plugins/cron/triggers.go @@ -55,19 +55,18 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { return nil } -// TriggerPreDelete stops cron for a given app -func TriggerPreDelete(appName string) error { +// TriggerPostDelete destroys the cron property for a given app container +func TriggerPostDelete(appName string) error { scheduler := common.GetAppScheduler(appName) _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "scheduler-cron-write", Args: []string{scheduler, appName}, StreamStdio: true, }) - return err -} + if err != nil { + return err + } -// TriggerPostDelete destroys the cron property for a given app container -func TriggerPostDelete(appName string) error { if err := common.PropertyDestroy("cron", appName); err != nil { return err } From ed851fe3996934059d9e4f2c4db7a2fbaa441785 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 01:11:29 -0400 Subject: [PATCH 28/97] refactor: warn when invalid cron entries might be written --- plugins/cron/cron.go | 2 +- plugins/scheduler-docker-local/functions.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index 667f5ecf7..77e9a6194 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -90,7 +90,7 @@ func FetchCronEntries(appName string) ([]TemplateCommand, error) { parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor) _, err := parser.Parse(c.Schedule) if err != nil { - return commands, fmt.Errorf("Invalid cron schedule %s: %s", c.Schedule, err.Error()) + return commands, fmt.Errorf("Invalid cron schedule for app %s (schedule %s): %s", appName, c.Schedule, err.Error()) } commands = append(commands, TemplateCommand{ diff --git a/plugins/scheduler-docker-local/functions.go b/plugins/scheduler-docker-local/functions.go index adf576314..6d4bca180 100644 --- a/plugins/scheduler-docker-local/functions.go +++ b/plugins/scheduler-docker-local/functions.go @@ -55,7 +55,8 @@ func generateCronEntries() ([]cron.TemplateCommand, error) { c, err := cron.FetchCronEntries(appName) if err != nil { results <- []cron.TemplateCommand{} - return err + common.LogWarn(err.Error()) + return nil } results <- c From 4ae9cca6f17c3a3c00c1f73d1409f88875bc45e6 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 02:19:08 -0400 Subject: [PATCH 29/97] feat: implement app-json-is-valid plugin trigger --- docs/development/plugin-triggers.md | 15 +++++++++ plugins/app-json/appjson.go | 35 ++++++++++++--------- plugins/app-json/triggers.go | 33 ++++++------------- plugins/cron/cron.go | 23 ++++++++++---- plugins/cron/report.go | 2 +- plugins/cron/subcommands.go | 4 +-- plugins/cron/triggers.go | 20 ++++++++++++ plugins/scheduler-docker-local/functions.go | 2 +- plugins/scheduler-k3s/triggers.go | 2 +- 9 files changed, 87 insertions(+), 49 deletions(-) diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index e37e22c90..c790eec4e 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -119,6 +119,21 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x # TODO ``` +### `app-json-is-valid` + +- Description: Checks to see if the provided app.json file is valid +- Invoked by: Appjson extraction during deployment +- Arguments: `$APP $APP_JSON_PATH` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +# TODO +``` + ### `app-maybe-create` - Description: Creates an app (gated by whether this is globally enabled or not) diff --git a/plugins/app-json/appjson.go b/plugins/app-json/appjson.go index 5ea123f1c..75f0ccced 100644 --- a/plugins/app-json/appjson.go +++ b/plugins/app-json/appjson.go @@ -200,21 +200,9 @@ func GetAppJSON(appName string) (AppJSON, error) { return AppJSON{}, nil } - b, err := os.ReadFile(getProcessSpecificAppJSONPath(appName)) - if err != nil { - return AppJSON{}, fmt.Errorf("Cannot read app.json file: %v", err) - } + appJSONPath := getProcessSpecificAppJSONPath(appName) - if strings.TrimSpace(string(b)) == "" { - return AppJSON{}, nil - } - - var appJSON AppJSON - if err = json.Unmarshal(b, &appJSON); err != nil { - return AppJSON{}, fmt.Errorf("Cannot parse app.json: %v", err) - } - - return appJSON, nil + return ReadAppJSON(appJSONPath) } func GetAutoscalingConfig(appName string, processType string, replicas int) (FormationAutoscaling, bool, error) { @@ -256,3 +244,22 @@ func GetAutoscalingConfig(appName string, processType string, replicas int) (For return autoscaling, true, nil } + +// ReadAppJSON reads an app.json file from a given path +func ReadAppJSON(path string) (AppJSON, error) { + b, err := os.ReadFile(path) + if err != nil { + return AppJSON{}, fmt.Errorf("Cannot read app.json file: %v", err) + } + + if strings.TrimSpace(string(b)) == "" { + return AppJSON{}, nil + } + + var appJSON AppJSON + if err = json.Unmarshal(b, &appJSON); err != nil { + return AppJSON{}, fmt.Errorf("Cannot parse app.json: %v", err) + } + + return appJSON, nil +} diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index 92a896e4e..c5d051b5e 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -1,7 +1,7 @@ package appjson import ( - "encoding/json" + "errors" "fmt" "os" "path" @@ -142,31 +142,16 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { } if hasAppJSON(appName) { - b, err := os.ReadFile(processSpecificAppJSON) + result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "app-json-is-valid", + Args: []string{appName, processSpecificAppJSON}, + }) if err != nil { - return err - } - - content := strings.TrimSpace(string(b)) - if content == "" { - return nil - } - - var appJSON AppJSON - if err := json.Unmarshal([]byte(content), &appJSON); err != nil { - return fmt.Errorf("Unable to unmarshal app.json: %v", err.Error()) - } - - if appJSON.Cron != nil { - for i, c := range appJSON.Cron { - if c.Command == "" { - return fmt.Errorf("Missing cron command for app %s (index %d)", appName, i) - } - - if c.Schedule == "" { - return fmt.Errorf("Missing cron schedule for app %s (index %d)", appName, i) - } + if result.StderrContents() != "" { + return errors.New(result.StderrContents()) } + + return err } } diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index 77e9a6194..5768065a3 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -62,21 +62,32 @@ func (t TemplateCommand) CronCommand() string { return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command) } +// FetchCronEntriesInput is the input for the FetchCronEntries function +type FetchCronEntriesInput struct { + AppName string + AppJSON *appjson.AppJSON +} + // FetchCronEntries returns a list of cron commands for a given app -func FetchCronEntries(appName string) ([]TemplateCommand, error) { +func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) { + appName := input.AppName commands := []TemplateCommand{} isMaintenance := reportComputedMaintenance(appName) == "true" - appJSON, err := appjson.GetAppJSON(appName) - if err != nil { - return commands, fmt.Errorf("Unable to fetch app.json for app %s: %s", appName, err.Error()) + if input.AppJSON == nil { + appJSON, err := appjson.GetAppJSON(appName) + if err != nil { + return commands, fmt.Errorf("Unable to fetch app.json for app %s: %s", appName, err.Error()) + } + + input.AppJSON = &appJSON } - if appJSON.Cron == nil { + if input.AppJSON.Cron == nil { return commands, nil } - for i, c := range appJSON.Cron { + for i, c := range input.AppJSON.Cron { if c.Command == "" { common.LogWarn(fmt.Sprintf("Missing cron command for app %s (index %d)", appName, i)) continue diff --git a/plugins/cron/report.go b/plugins/cron/report.go index 769ed59a6..3514b8f53 100644 --- a/plugins/cron/report.go +++ b/plugins/cron/report.go @@ -41,7 +41,7 @@ func reportMailto(_ string) string { } func reportTasks(appName string) string { - c, _ := FetchCronEntries(appName) + c, _ := FetchCronEntries(FetchCronEntriesInput{AppName: appName}) return strconv.Itoa(len(c)) } diff --git a/plugins/cron/subcommands.go b/plugins/cron/subcommands.go index aede7d39b..2d5717b4b 100644 --- a/plugins/cron/subcommands.go +++ b/plugins/cron/subcommands.go @@ -26,7 +26,7 @@ func CommandList(appName string, format string) error { return fmt.Errorf("Invalid format specified, supported formats: json, stdout") } - entries, err := FetchCronEntries(appName) + entries, err := FetchCronEntries(FetchCronEntriesInput{AppName: appName}) if err != nil { return err } @@ -79,7 +79,7 @@ func CommandRun(appName string, cronID string, detached bool) error { return err } - entries, err := FetchCronEntries(appName) + entries, err := FetchCronEntries(FetchCronEntriesInput{AppName: appName}) if err != nil { return err } diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go index 383a7cd3a..a0f192e74 100644 --- a/plugins/cron/triggers.go +++ b/plugins/cron/triggers.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + appjson "github.com/dokku/dokku/plugins/app-json" "github.com/dokku/dokku/plugins/common" ) @@ -23,6 +24,25 @@ func TriggerCronGetProperty(appName string, key string) error { return nil } +// TriggerAppJSONIsValid validates the cron entries for a given app +func TriggerAppJSONIsValid(appName string, appJSONPath string) error { + if !common.FileExists(appJSONPath) { + return nil + } + + appJSON, err := appjson.ReadAppJSON(appJSONPath) + if err != nil { + return err + } + + _, err = FetchCronEntries(FetchCronEntriesInput{AppName: appName, AppJSON: &appJSON}) + if err != nil { + return err + } + + return nil +} + // TriggerInstall runs the install step for the cron plugin func TriggerInstall() error { if err := common.PropertySetup("cron"); err != nil { diff --git a/plugins/scheduler-docker-local/functions.go b/plugins/scheduler-docker-local/functions.go index 6d4bca180..e11d7c035 100644 --- a/plugins/scheduler-docker-local/functions.go +++ b/plugins/scheduler-docker-local/functions.go @@ -52,7 +52,7 @@ func generateCronEntries() ([]cron.TemplateCommand, error) { return nil } - c, err := cron.FetchCronEntries(appName) + c, err := cron.FetchCronEntries(cron.FetchCronEntriesInput{AppName: appName}) if err != nil { results <- []cron.TemplateCommand{} common.LogWarn(err.Error()) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 22fec8b5a..d81094cbe 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -266,7 +266,7 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e workingDir := common.GetWorkingDir(appName, image) - allCronEntries, err := cron.FetchCronEntries(appName) + allCronEntries, err := cron.FetchCronEntries(cron.FetchCronEntriesInput{AppName: appName}) if err != nil { return fmt.Errorf("Error fetching cron entries: %w", err) } From 2324cb888bcf74e79d978e2c0b924bb76fbfb4d1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 20:53:20 -0400 Subject: [PATCH 30/97] refactor: check for extracted file and add logging message --- plugins/app-json/triggers.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index c5d051b5e..28b37a7eb 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -141,7 +141,8 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { } } - if hasAppJSON(appName) { + if common.FileExists(processSpecificAppJSON) { + common.LogInfo1("Validating app.json file") result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "app-json-is-valid", Args: []string{appName, processSpecificAppJSON}, From 7c2df655140c178c906d24459162afd0b0ce933d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 21:17:58 -0400 Subject: [PATCH 31/97] fix: reference new packeto buildpack urls See https://blog.paketo.io/posts/paketo-gcr-registry-sunset/ for details. --- tests/apps/python/project2.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/apps/python/project2.toml b/tests/apps/python/project2.toml index 67df953e6..ff4dc30cf 100644 --- a/tests/apps/python/project2.toml +++ b/tests/apps/python/project2.toml @@ -10,7 +10,7 @@ uri = "./cnb/1" uri = "./cnb/1" [[build.buildpacks]] -uri = "gcr.io/paketo-buildpacks/python" +uri = "docker.io/paketobuildpacks/python" [[order.group]] -uri = "gcr.io/paketo-buildpacks/procfile" +uri = "docker.io/paketobuildpacks/procfile" From 7cd077a79b60c55e59e1ca84b5fa29021ddf30e1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 19 Jun 2025 21:19:24 -0400 Subject: [PATCH 32/97] debug: stream stdout/stderr --- plugins/app-json/triggers.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index 28b37a7eb..253f89722 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -144,8 +144,10 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { if common.FileExists(processSpecificAppJSON) { common.LogInfo1("Validating app.json file") result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "app-json-is-valid", - Args: []string{appName, processSpecificAppJSON}, + Trigger: "app-json-is-valid", + Args: []string{appName, processSpecificAppJSON}, + StreamStdout: true, + StreamStderr: true, }) if err != nil { if result.StderrContents() != "" { From 7e37f9dc79d2200ae6d42802beb2501cb6936957 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 23 Jun 2025 00:22:38 -0400 Subject: [PATCH 33/97] fix: add missing trigger --- plugins/app-json/triggers.go | 1 - plugins/cron/Makefile | 2 +- plugins/cron/src/triggers/triggers.go | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index 253f89722..08bd24404 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -142,7 +142,6 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { } if common.FileExists(processSpecificAppJSON) { - common.LogInfo1("Validating app.json file") result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "app-json-is-valid", Args: []string{appName, processSpecificAppJSON}, diff --git a/plugins/cron/Makefile b/plugins/cron/Makefile index 920941136..98eeb5fd2 100644 --- a/plugins/cron/Makefile +++ b/plugins/cron/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/list subcommands/report subcommands/run subcommands/set -TRIGGERS = triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/scheduler-stop +TRIGGERS = triggers/app-json-is-valid triggers/cron-get-property triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = cron diff --git a/plugins/cron/src/triggers/triggers.go b/plugins/cron/src/triggers/triggers.go index 6e0de5e7a..1a65cec73 100644 --- a/plugins/cron/src/triggers/triggers.go +++ b/plugins/cron/src/triggers/triggers.go @@ -19,6 +19,10 @@ func main() { var err error switch trigger { + case "app-json-is-valid": + appName := flag.Arg(0) + appJSONPath := flag.Arg(1) + err = cron.TriggerAppJSONIsValid(appName, appJSONPath) case "cron-get-property": appName := flag.Arg(0) property := flag.Arg(1) From 885178eed5d85d7c6da374e42e454bb7310b9766 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 23 Jun 2025 01:03:10 -0400 Subject: [PATCH 34/97] feat: turn warnings to failures when running validation against an app.json file --- plugins/cron/cron.go | 13 +++++++++++-- plugins/cron/triggers.go | 6 +++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/cron/cron.go b/plugins/cron/cron.go index 5768065a3..8013debe1 100644 --- a/plugins/cron/cron.go +++ b/plugins/cron/cron.go @@ -64,8 +64,9 @@ func (t TemplateCommand) CronCommand() string { // FetchCronEntriesInput is the input for the FetchCronEntries function type FetchCronEntriesInput struct { - AppName string - AppJSON *appjson.AppJSON + AppName string + AppJSON *appjson.AppJSON + WarnToFailure bool } // FetchCronEntries returns a list of cron commands for a given app @@ -89,11 +90,19 @@ func FetchCronEntries(input FetchCronEntriesInput) ([]TemplateCommand, error) { for i, c := range input.AppJSON.Cron { if c.Command == "" { + if input.WarnToFailure { + return commands, fmt.Errorf("Missing cron command for app %s (index %d)", appName, i) + } + common.LogWarn(fmt.Sprintf("Missing cron command for app %s (index %d)", appName, i)) continue } if c.Schedule == "" { + if input.WarnToFailure { + return commands, 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)) continue } diff --git a/plugins/cron/triggers.go b/plugins/cron/triggers.go index a0f192e74..499405913 100644 --- a/plugins/cron/triggers.go +++ b/plugins/cron/triggers.go @@ -35,7 +35,11 @@ func TriggerAppJSONIsValid(appName string, appJSONPath string) error { return err } - _, err = FetchCronEntries(FetchCronEntriesInput{AppName: appName, AppJSON: &appJSON}) + _, err = FetchCronEntries(FetchCronEntriesInput{ + AppName: appName, + AppJSON: &appJSON, + WarnToFailure: true, + }) if err != nil { return err } From 1eb41f32dc880ec437702e1054c07fda5b7d3786 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 2 Feb 2025 22:27:13 -0500 Subject: [PATCH 35/97] feat: only redeploy formations that have had their values changed Rather than redeploy every formation, only deploy formations that have had their values changed from the defaults. This still doesn't handle the case where we touch existing processes when scaling _up_, but should decrease the number of services touched otherwise during scaling. Also note that there are no tests for this new behavior yet (tests incoming). Refs #7396 --- plugins/ps/functions.go | 78 ++++++++++++++++++++++++++++++--------- plugins/ps/subcommands.go | 8 +++- plugins/ps/triggers.go | 8 +++- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/plugins/ps/functions.go b/plugins/ps/functions.go index 3bb7f1235..b6af4f6dc 100644 --- a/plugins/ps/functions.go +++ b/plugins/ps/functions.go @@ -247,33 +247,77 @@ func scaleReport(appName string) error { return nil } -func scaleSet(appName string, skipDeploy bool, clearExisting bool, processTuples []string) error { - formations, err := parseProcessTuples(processTuples) +// scaleSetInput is the input for the scaleSet function +type scaleSetInput struct { + // appName is the name of the app to scale + appName string + + // skipDeploy is a flag to skip the deploy phase + skipDeploy bool + + // clearExisting is a flag to clear the existing scale + clearExisting bool + + // processTuples is a list of process tuples to scale + processTuples []string + + // deployOnlyChanged is a flag to deploy only the changed formations + deployOnlyChanged bool +} + +func scaleSet(input scaleSetInput) error { + existingFormations, err := getFormations(input.appName) if err != nil { return err } - if err := updateScale(appName, clearExisting, formations); err != nil { - return err - } - - if skipDeploy { - return nil - } - - if !common.IsDeployed(appName) { - return nil - } - - imageTag, err := common.GetRunningImageTag(appName, "") + formations, err := parseProcessTuples(input.processTuples) if err != nil { return err } - for _, formation := range formations { + if err := updateScale(input.appName, input.clearExisting, formations); err != nil { + return err + } + + if input.skipDeploy { + return nil + } + + if !common.IsDeployed(input.appName) { + return nil + } + + imageTag, err := common.GetRunningImageTag(input.appName, "") + if err != nil { + return err + } + + changedFormations := FormationSlice{} + if input.deployOnlyChanged { + for _, formation := range formations { + isChanged := true + for _, existingFormation := range existingFormations { + if existingFormation.ProcessType == formation.ProcessType { + if existingFormation.Quantity == formation.Quantity { + isChanged = false + break + } + } + } + + if isChanged { + changedFormations = append(changedFormations, formation) + } + } + } else { + changedFormations = formations + } + + for _, formation := range changedFormations { _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "deploy", - Args: []string{appName, imageTag, formation.ProcessType}, + Args: []string{input.appName, imageTag, formation.ProcessType}, StreamStdio: true, }) if err != nil { diff --git a/plugins/ps/subcommands.go b/plugins/ps/subcommands.go index 6c5882e5e..783a26161 100644 --- a/plugins/ps/subcommands.go +++ b/plugins/ps/subcommands.go @@ -160,7 +160,13 @@ func CommandScale(appName string, skipDeploy bool, processTuples []string) error } common.LogInfo1(fmt.Sprintf("Scaling %s processes: %s", appName, strings.Join(processTuples, " "))) - return scaleSet(appName, skipDeploy, false, processTuples) + return scaleSet(scaleSetInput{ + appName: appName, + skipDeploy: skipDeploy, + clearExisting: false, + processTuples: processTuples, + deployOnlyChanged: true, + }) } // CommandSet sets or clears a ps property for an app diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 90a4e001b..4d788827c 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -339,7 +339,13 @@ func TriggerPsCurrentScale(appName string) error { // TriggerPsSetScale configures the scale parameters for a given app func TriggerPsSetScale(appName string, skipDeploy bool, clearExisting bool, processTuples []string) error { - return scaleSet(appName, skipDeploy, clearExisting, processTuples) + return scaleSet(scaleSetInput{ + appName: appName, + skipDeploy: skipDeploy, + clearExisting: clearExisting, + processTuples: processTuples, + deployOnlyChanged: false, + }) } func TriggerPsGetProperty(appName string, property string) error { From 5cd86d23e67c5976d3884ab21e59a22f76c88395 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 30 Jun 2025 01:41:37 -0400 Subject: [PATCH 36/97] tests: add tests for scaling without restarting untouched process types --- tests/unit/ps-dockerfile-2.bats | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/unit/ps-dockerfile-2.bats b/tests/unit/ps-dockerfile-2.bats index dd4cf314d..7a26fe212 100644 --- a/tests/unit/ps-dockerfile-2.bats +++ b/tests/unit/ps-dockerfile-2.bats @@ -171,3 +171,18 @@ teardown() { assert_success done } + +@test "(ps:scale) dockerfile unchanged process" { + run deploy_app dockerfile + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku ps:scale $TEST_APP worker=2" + echo "output: $output" + echo "status: $status" + assert_output_contains "Scaling $TEST_APP processes: worker=2" + assert_output_contains "Deploying worker" + assert_output_contains "Deploying web" 0 + assert_success +} From a331d27945470e40b284feebc986d2470cc191a4 Mon Sep 17 00:00:00 2001 From: Udi Oron Date: Sat, 1 Feb 2025 20:52:08 +0200 Subject: [PATCH 37/97] feat(nginx-vhosts): add support for custom nginx service command --- plugins/nginx-vhosts/internal-functions | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/nginx-vhosts/internal-functions b/plugins/nginx-vhosts/internal-functions index 90a77fb42..5485c345a 100755 --- a/plugins/nginx-vhosts/internal-functions +++ b/plugins/nginx-vhosts/internal-functions @@ -729,9 +729,13 @@ fn-nginx-vhosts-nginx-is-running() { fn-nginx-vhosts-nginx-init-cmd() { declare desc="start nginx for given distros" declare CMD="$1" - local NGINX_INIT_NAME - NGINX_INIT_NAME=nginx + if [[ -n "$DOKKU_NGINX_CUSTOM_CMD" ]]; then + $DOKKU_NGINX_CUSTOM_CMD $CMD + return + fi + + local NGINX_INIT_NAME=nginx if fn-nginx-vhosts-uses-openresty; then NGINX_INIT_NAME=openresty fi From 06de81b61ba1175ea2e8d20e64bbbb62526ee273 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 30 Jun 2025 03:29:43 -0400 Subject: [PATCH 38/97] refactor: allow custom nginx command to be set as an nginx property This avoids adding a new environment variable in favor of a script that can be called both globally as well as on a per-app basis. --- docs/networking/proxies/nginx.md | 1 + plugins/nginx-vhosts/internal-functions | 20 ++++++++- plugins/nginx-vhosts/nginx_vhosts.go | 43 +++++++++++++------ .../src/nginx-property/nginx-property.go | 6 +++ plugins/nginx-vhosts/subcommands/set | 6 +-- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/docs/networking/proxies/nginx.md b/docs/networking/proxies/nginx.md index ed1d64d47..bd6b5d92a 100644 --- a/docs/networking/proxies/nginx.md +++ b/docs/networking/proxies/nginx.md @@ -332,6 +332,7 @@ Changing these value globally or on a per-app basis will require rebuilding the | keepalive-timeout | `75s` | string | Timeout (with units) during which a keep-alive client connection will stay open on the server side | | lingering-timeout | `5s` | string | Timeout (with units) is the maximum waiting time for more client data to arrive | | nginx-conf-sigil-path | `nginx.conf.sigil` | string | Path in the repository to the `nginx.conf.sigil` file | +| nginx-service-command | | string | Full path to an executable script or binary to invoke instead of the system's init process when interacting with nginx | | proxy-buffer-size | `8k` (# is os pagesize) | string | Size of the buffer used for reading the first part of proxied server response | | proxy-buffering | `on` | string | Enables or disables buffering of responses from the proxied server | | proxy-buffers | `8 8k` | string | Number and size of the buffers used for reading the proxied server response, for a single connection | diff --git a/plugins/nginx-vhosts/internal-functions b/plugins/nginx-vhosts/internal-functions index 5485c345a..1950e199e 100755 --- a/plugins/nginx-vhosts/internal-functions +++ b/plugins/nginx-vhosts/internal-functions @@ -327,6 +327,21 @@ fn-nginx-global-nginx-conf-sigil-path() { fn-get-property --global "nginx-conf-sigil-path" } +fn-nginx-nginx-service-command() { + declare APP="$1" + fn-get-property --app "$APP" "nginx-service-command" +} + +fn-nginx-computed-nginx-service-command() { + declare APP="$1" + fn-get-property --computed --app "$APP" "nginx-service-command" +} + +fn-nginx-global-nginx-service-command() { + declare APP="$1" + fn-get-property --global "nginx-service-command" +} + fn-nginx-proxy-buffer-size() { declare desc="get the configured proxy buffer size" declare APP="$1" @@ -730,8 +745,9 @@ fn-nginx-vhosts-nginx-init-cmd() { declare desc="start nginx for given distros" declare CMD="$1" - if [[ -n "$DOKKU_NGINX_CUSTOM_CMD" ]]; then - $DOKKU_NGINX_CUSTOM_CMD $CMD + local NGINX_SERVICE_COMMAND="$(fn-nginx-computed-nginx-service-command "$APP")" + if [[ -n "$NGINX_SERVICE_COMMAND" ]]; then + "$NGINX_SERVICE_COMMAND" $CMD return fi diff --git a/plugins/nginx-vhosts/nginx_vhosts.go b/plugins/nginx-vhosts/nginx_vhosts.go index 4d7d17ac6..8c25ac1a7 100644 --- a/plugins/nginx-vhosts/nginx_vhosts.go +++ b/plugins/nginx-vhosts/nginx_vhosts.go @@ -234,6 +234,36 @@ func AppNginxConfSigilPath(appName string) string { return common.PropertyGet("nginx", appName, "nginx-conf-sigil-path") } +func ComputedNginxConfSigilPath(appName string) string { + appValue := AppNginxConfSigilPath(appName) + if appValue != "" { + return appValue + } + + return GlobalNginxConfSigilPath() +} + +func GlobalNginxConfSigilPath() string { + return common.PropertyGetDefault("nginx", "--global", "nginx-conf-sigil-path", "nginx.conf.sigil") +} + +func AppNginxServiceCommand(appName string) string { + return common.PropertyGet("nginx", appName, "nginx-service-command") +} + +func ComputedNginxServiceCommand(appName string) string { + appValue := AppNginxServiceCommand(appName) + if appValue != "" { + return appValue + } + + return GlobalNginxServiceCommand() +} + +func GlobalNginxServiceCommand() string { + return common.PropertyGetDefault("nginx", "--global", "nginx-service-command", "") +} + func AppKeepaliveTimeout(appName string) string { return common.PropertyGet("nginx", appName, "keepalive-timeout") } @@ -268,19 +298,6 @@ func GlobalLingeringTimeout() string { return common.PropertyGetDefault("nginx", "--global", "lingering-timeout", "5s") } -func ComputedNginxConfSigilPath(appName string) string { - appValue := AppNginxConfSigilPath(appName) - if appValue != "" { - return appValue - } - - return GlobalNginxConfSigilPath() -} - -func GlobalNginxConfSigilPath() string { - return common.PropertyGetDefault("nginx", "--global", "nginx-conf-sigil-path", "nginx.conf.sigil") -} - func AppProxyBufferSize(appName string) string { return common.PropertyGet("nginx", appName, "proxy-buffer-size") } diff --git a/plugins/nginx-vhosts/src/nginx-property/nginx-property.go b/plugins/nginx-vhosts/src/nginx-property/nginx-property.go index 6270d97c2..47c3bbd49 100644 --- a/plugins/nginx-vhosts/src/nginx-property/nginx-property.go +++ b/plugins/nginx-vhosts/src/nginx-property/nginx-property.go @@ -67,6 +67,8 @@ func appValue(appName string, property string) string { value = nginx_vhosts.AppLingeringTimeout(appName) case "nginx-conf-sigil-path": value = nginx_vhosts.AppNginxConfSigilPath(appName) + case "nginx-service-command": + value = nginx_vhosts.AppNginxServiceCommand(appName) case "proxy-buffer-size": value = nginx_vhosts.AppProxyBufferSize(appName) case "proxy-buffering": @@ -133,6 +135,8 @@ func computedValue(appName string, property string) string { value = nginx_vhosts.ComputedLingeringTimeout(appName) case "nginx-conf-sigil-path": value = nginx_vhosts.ComputedNginxConfSigilPath(appName) + case "nginx-service-command": + value = nginx_vhosts.ComputedNginxServiceCommand(appName) case "proxy-buffer-size": value = nginx_vhosts.ComputedProxyBufferSize(appName) case "proxy-buffering": @@ -199,6 +203,8 @@ func globalValue(appName string, property string) string { value = nginx_vhosts.ComputedLingeringTimeout(appName) case "nginx-conf-sigil-path": value = nginx_vhosts.GlobalNginxConfSigilPath() + case "nginx-service-command": + value = nginx_vhosts.GlobalNginxServiceCommand() case "proxy-buffer-size": value = nginx_vhosts.GlobalProxyBufferSize() case "proxy-buffering": diff --git a/plugins/nginx-vhosts/subcommands/set b/plugins/nginx-vhosts/subcommands/set index 49e2e541b..12cfaaab5 100755 --- a/plugins/nginx-vhosts/subcommands/set +++ b/plugins/nginx-vhosts/subcommands/set @@ -9,13 +9,13 @@ cmd-nginx-set() { declare cmd="nginx:set" [[ "$1" == "$cmd" ]] && shift 1 declare APP="$1" KEY="$2" VALUE="$3" - local VALID_KEYS=("access-log-format" "access-log-path" "bind-address-ipv4" "bind-address-ipv6" "client-body-timeout" "client-header-timeout" "client-max-body-size" "disable-custom-config" "error-log-path" "hsts" "hsts-include-subdomains" "hsts-preload" "hsts-max-age" "keepalive-timeout" "lingering-timeout" "nginx-conf-sigil-path" "proxy-buffer-size" "proxy-buffering" "proxy-buffers" "proxy-busy-buffers-size" "proxy-connect-timeout" "proxy-read-timeout" "proxy-send-timeout" "send-timeout" "underscore-in-headers" "x-forwarded-for-value" "x-forwarded-port-value" "x-forwarded-proto-value" "x-forwarded-ssl") - local GLOBAL_KEYS=("access-log-format" "access-log-path" "bind-address-ipv4" "bind-address-ipv6" "client-body-timeout" "client-header-timeout" "client-max-body-size" "disable-custom-config" "error-log-path" "hsts-include-subdomains" "hsts-max-age" "hsts-preload" "hsts" "keepalive-timeout" "lingering-timeout" "nginx-conf-sigil-path" "proxy-buffer-size" "proxy-buffering" "proxy-buffers" "proxy-busy-buffers-size" "proxy-connect-timeout" "proxy-read-timeout" "proxy-send-timeout" "send-timeout" "underscore-in-headers" "x-forwarded-for-value" "x-forwarded-port-value" "x-forwarded-proto-value" "x-forwarded-ssl") + local VALID_KEYS=("access-log-format" "access-log-path" "bind-address-ipv4" "bind-address-ipv6" "client-body-timeout" "client-header-timeout" "client-max-body-size" "disable-custom-config" "error-log-path" "hsts" "hsts-include-subdomains" "hsts-preload" "hsts-max-age" "keepalive-timeout" "lingering-timeout" "nginx-conf-sigil-path" "nginx-service-command" "proxy-buffer-size" "proxy-buffering" "proxy-buffers" "proxy-busy-buffers-size" "proxy-connect-timeout" "proxy-read-timeout" "proxy-send-timeout" "send-timeout" "underscore-in-headers" "x-forwarded-for-value" "x-forwarded-port-value" "x-forwarded-proto-value" "x-forwarded-ssl") + local GLOBAL_KEYS=("access-log-format" "access-log-path" "bind-address-ipv4" "bind-address-ipv6" "client-body-timeout" "client-header-timeout" "client-max-body-size" "disable-custom-config" "error-log-path" "hsts-include-subdomains" "hsts-max-age" "hsts-preload" "hsts" "keepalive-timeout" "lingering-timeout" "nginx-conf-sigil-path" "nginx-service-command" "proxy-buffer-size" "proxy-buffering" "proxy-buffers" "proxy-busy-buffers-size" "proxy-connect-timeout" "proxy-read-timeout" "proxy-send-timeout" "send-timeout" "underscore-in-headers" "x-forwarded-for-value" "x-forwarded-port-value" "x-forwarded-proto-value" "x-forwarded-ssl") [[ -z "$KEY" ]] && dokku_log_fail "No key specified" if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then - dokku_log_fail "Invalid key specified, valid keys include: access-log-format, access-log-path, bind-address-ipv4, bind-address-ipv6, client-body-timeout, client-header-timeout, client-max-body-size, disable-custom-config, error-log-path, hsts, hsts-include-subdomains, hsts-preload, hsts-max-age, keepalive-timeout, lingering-timeout, nginx-conf-sigil-path, proxy-buffer-size, proxy-buffering, proxy-buffers, proxy-busy-buffers-size, proxy-connect-timeout, proxy-read-timeout, proxy-send-timeout, send-timeout, underscore-in-headers, x-forwarded-for-value, x-forwarded-port-value, x-forwarded-proto-value, x-forwarded-ssl" + dokku_log_fail "Invalid key specified, valid keys include: access-log-format, access-log-path, bind-address-ipv4, bind-address-ipv6, client-body-timeout, client-header-timeout, client-max-body-size, disable-custom-config, error-log-path, hsts, hsts-include-subdomains, hsts-preload, hsts-max-age, keepalive-timeout, lingering-timeout, nginx-conf-sigil-path, nginx-service-command, proxy-buffer-size, proxy-buffering, proxy-buffers, proxy-busy-buffers-size, proxy-connect-timeout, proxy-read-timeout, proxy-send-timeout, send-timeout, underscore-in-headers, x-forwarded-for-value, x-forwarded-port-value, x-forwarded-proto-value, x-forwarded-ssl" fi if ! fn-in-array "$KEY" "${GLOBAL_KEYS[@]}"; then From b97086a8bd18a743bffd4509e3ee581c1a5f5ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Justen=20=28=40turicas=29?= Date: Sun, 5 Jan 2025 14:27:33 -0300 Subject: [PATCH 39/97] domains:clear now removes ALL app domains --- docs/configuration/domains.md | 6 +++--- docs/networking/proxy-management.md | 2 +- plugins/domains/functions | 3 +-- plugins/domains/help-functions | 2 +- plugins/domains/subcommands/clear | 2 +- plugins/domains/subcommands/clear-global | 4 ++-- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/configuration/domains.md b/docs/configuration/domains.md index 71ac8a98f..44a7627f8 100644 --- a/docs/configuration/domains.md +++ b/docs/configuration/domains.md @@ -6,8 +6,8 @@ ``` domains:add [ ...] # Add domains to app domains:add-global [ ...] # Add global domain names -domains:clear # Clear all domains for app -domains:clear-global # Clear global domain names +domains:clear # Remove all app domains +domains:clear-global # Remove all global domains domains:disable # Disable VHOST support domains:enable # Enable VHOST support domains:remove [ ...] # Remove domains from app @@ -67,7 +67,7 @@ dokku domains:add node-js-app dokku.me # list custom domains for app dokku domains:report node-js-app -# clear all custom domains for app +# delete all domains for app dokku domains:clear node-js-app # remove a custom domain from app diff --git a/docs/networking/proxy-management.md b/docs/networking/proxy-management.md index 6b1ec7f55..cce2b0835 100644 --- a/docs/networking/proxy-management.md +++ b/docs/networking/proxy-management.md @@ -150,7 +150,7 @@ At this time, the following dokku commands are used to interact with a complete - `domains:add`: Adds a given domain to an app. - triggers: `post-domains-update` -- `domains:clear`: Clears out an app's associated domains. +- `domains:clear`: Remove all app domains. - triggers: `post-domains-update` - `domains:disable`: Disables domains for an app. - triggers: `pre-disable-vhost` diff --git a/plugins/domains/functions b/plugins/domains/functions index 0798f48f3..41257e709 100755 --- a/plugins/domains/functions +++ b/plugins/domains/functions @@ -24,9 +24,8 @@ fn-domains-clear-app() { local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" rm -f "$APP_VHOST_PATH" - domains_setup "$APP" plugn trigger post-domains-update "$APP" "clear" - dokku_log_info1 "Cleared domains in $APP" + dokku_log_info1 "Deleted all domains for $APP" } fn-domains-disable() { diff --git a/plugins/domains/help-functions b/plugins/domains/help-functions index 0f66a8734..72b8d9474 100755 --- a/plugins/domains/help-functions +++ b/plugins/domains/help-functions @@ -29,7 +29,7 @@ fn-help-content() { cat < [ ...], Add domains to app domains:add-global [ ...], Add global domain names - domains:clear , Clear all domains for app + domains:clear , Remove all app domains domains:clear-global, Clear global domain names domains:disable , Disable VHOST support domains:enable , Enable VHOST support diff --git a/plugins/domains/subcommands/clear b/plugins/domains/subcommands/clear index c6065abb2..3b18beaa0 100755 --- a/plugins/domains/subcommands/clear +++ b/plugins/domains/subcommands/clear @@ -5,7 +5,7 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/domains/functions" cmd-domains-clear() { - declare desc="clear all app domains" + declare desc="remove all app domains" declare cmd="domains:clear" [[ "$1" == "$cmd" ]] && shift 1 declare APP="$1" diff --git a/plugins/domains/subcommands/clear-global b/plugins/domains/subcommands/clear-global index ff7ae757b..3fd4815b5 100755 --- a/plugins/domains/subcommands/clear-global +++ b/plugins/domains/subcommands/clear-global @@ -5,11 +5,11 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/domains/functions" cmd-domains-clear-global() { - declare desc="clear global domain names via command line" + declare desc="remove all global domains" declare cmd="domains:clear-global" [[ "$1" == "$cmd" ]] && shift 1 - dokku_log_info1_quiet "Clearing global domains" + dokku_log_info1_quiet "Deleting global domains" domains_clear_global "$@" } From 67311264d76a87802e3b00e45df6ab4eb20dcae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Justen=20=28=40turicas=29?= Date: Sun, 5 Jan 2025 14:28:31 -0300 Subject: [PATCH 40/97] Implement domains:reset --- docs/configuration/domains.md | 4 ++++ docs/development/plugin-triggers.md | 2 +- docs/networking/proxy-management.md | 2 ++ plugins/domains/help-functions | 1 + plugins/domains/subcommands/reset | 22 ++++++++++++++++++++++ tests/unit/domains.bats | 4 ++-- 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100755 plugins/domains/subcommands/reset diff --git a/docs/configuration/domains.md b/docs/configuration/domains.md index 44a7627f8..851dd2894 100644 --- a/docs/configuration/domains.md +++ b/docs/configuration/domains.md @@ -13,6 +13,7 @@ domains:enable # Enable VHOST support domains:remove [ ...] # Remove domains from app domains:remove-global [ ...] # Remove global domain names domains:report [|--global] [] # Displays a domains report for one or more apps +domains:reset # Reset app domains to global-configured domains domains:set [ ...] # Set domains for app domains:set-global [ ...] # Set global domain names ``` @@ -75,6 +76,9 @@ dokku domains:remove node-js-app dokku.me # set all custom domains for app dokku domains:set node-js-app dokku.me dokku.org + +# delete all app domains and configure available global domains on it +dokku domains:reset node-js-app ``` ## Displaying domains reports for an app diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index c790eec4e..43bbab731 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1699,7 +1699,7 @@ curl "http://httpstat.us/200" ### `post-domains-update` - Description: Allows you to run commands once the domain for an app has been updated. It also sends in the command that has been used. This can be "add", "clear" or "remove". The third argument will be the optional list of domains -- Invoked by: `dokku domains:add`, `dokku domains:clear`, `dokku domains:remove`, `dokku domains:set` +- Invoked by: `dokku domains:add`, `dokku domains:clear`, `dokku domains:remove`, `dokku domains:reset`, `dokku domains:set` - Arguments: `$APP` `action name` `domains` - Example: diff --git a/docs/networking/proxy-management.md b/docs/networking/proxy-management.md index cce2b0835..e10851d7f 100644 --- a/docs/networking/proxy-management.md +++ b/docs/networking/proxy-management.md @@ -158,6 +158,8 @@ At this time, the following dokku commands are used to interact with a complete - triggers: `pre-enable-vhost` - `domains:remove`: Removes a domain from an app. - triggers: `post-domains-update` +- `domains:reset`: Reset app domains to global-configured domains. + - triggers: `post-domains-update` - `domains:set`: Sets all domains for a given app. - triggers: `post-domains-update` - `proxy:build-config`: Builds - or rebuilds - external proxy configuration. diff --git a/plugins/domains/help-functions b/plugins/domains/help-functions index 72b8d9474..5a43671da 100755 --- a/plugins/domains/help-functions +++ b/plugins/domains/help-functions @@ -36,6 +36,7 @@ fn-help-content() { domains:remove [ ...], Remove domains from app domains:remove-global [ ...], Remove global domain names domains:report [|--global] [], Displays a domains report for one or more apps + domains:reset , Reset app domains to global-configured domains domains:set [ ...], Set domains for app domains:set-global [ ...], Set global domain names help_content diff --git a/plugins/domains/subcommands/reset b/plugins/domains/subcommands/reset new file mode 100755 index 000000000..2136ef5a9 --- /dev/null +++ b/plugins/domains/subcommands/reset @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/domains/functions" + +cmd-domains-reset() { + declare desc="reset app domains to global-configured domains" + declare cmd="domains:reset" + [[ "$1" == "$cmd" ]] && shift 1 + declare APP="$1" + + verify_app_name "$APP" + local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" + + rm -f "$APP_VHOST_PATH" + domains_setup "$APP" + plugn trigger post-domains-update "$APP" "reset" + dokku_log_info1 "Reset domains for $APP" +} + +cmd-domains-reset "$@" diff --git a/tests/unit/domains.bats b/tests/unit/domains.bats index 24f0571d5..b8fa384eb 100644 --- a/tests/unit/domains.bats +++ b/tests/unit/domains.bats @@ -188,7 +188,7 @@ teardown() { assert_output_contains a--domain.with--hyphens } -@test "(domains) domains:clear" { +@test "(domains) domains:reset" { run /bin/bash -c "dokku domains:add $TEST_APP test.app.${DOKKU_DOMAIN}" echo "output: $output" echo "status: $status" @@ -199,7 +199,7 @@ teardown() { echo "status: $status" assert_success - run /bin/bash -c "dokku domains:clear $TEST_APP" + run /bin/bash -c "dokku domains:reset $TEST_APP" echo "output: $output" echo "status: $status" assert_success From 624acb8e184b4e8873dd7251a9cb3a0716e547ff Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:12:35 -0400 Subject: [PATCH 41/97] chore: revert doc change --- docs/configuration/domains.md | 4 ++-- plugins/domains/help-functions | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration/domains.md b/docs/configuration/domains.md index 851dd2894..f473fa6cd 100644 --- a/docs/configuration/domains.md +++ b/docs/configuration/domains.md @@ -6,8 +6,8 @@ ``` domains:add [ ...] # Add domains to app domains:add-global [ ...] # Add global domain names -domains:clear # Remove all app domains -domains:clear-global # Remove all global domains +domains:clear # Clear all domains for app +domains:clear-global # Clear global domain names domains:disable # Disable VHOST support domains:enable # Enable VHOST support domains:remove [ ...] # Remove domains from app diff --git a/plugins/domains/help-functions b/plugins/domains/help-functions index 5a43671da..028c11204 100755 --- a/plugins/domains/help-functions +++ b/plugins/domains/help-functions @@ -29,7 +29,7 @@ fn-help-content() { cat < [ ...], Add domains to app domains:add-global [ ...], Add global domain names - domains:clear , Remove all app domains + domains:clear , Clear all domains for app domains:clear-global, Clear global domain names domains:disable , Disable VHOST support domains:enable , Enable VHOST support From a062e283fe64ebd90725713b4385f3a4fb0eb76f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:14:30 -0400 Subject: [PATCH 42/97] chore: revert doc change --- docs/networking/proxy-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/networking/proxy-management.md b/docs/networking/proxy-management.md index e10851d7f..173b2e868 100644 --- a/docs/networking/proxy-management.md +++ b/docs/networking/proxy-management.md @@ -150,7 +150,7 @@ At this time, the following dokku commands are used to interact with a complete - `domains:add`: Adds a given domain to an app. - triggers: `post-domains-update` -- `domains:clear`: Remove all app domains. +- `domains:clear`: Clears out an app's associated domains. - triggers: `post-domains-update` - `domains:disable`: Disables domains for an app. - triggers: `pre-disable-vhost` From 6a0701fa6591f0381413d81649f3818ecd921055 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:15:27 -0400 Subject: [PATCH 43/97] chore: revert doc change --- plugins/domains/functions | 2 +- plugins/domains/subcommands/clear | 2 +- plugins/domains/subcommands/clear-global | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/domains/functions b/plugins/domains/functions index 41257e709..cf705a243 100755 --- a/plugins/domains/functions +++ b/plugins/domains/functions @@ -25,7 +25,7 @@ fn-domains-clear-app() { rm -f "$APP_VHOST_PATH" plugn trigger post-domains-update "$APP" "clear" - dokku_log_info1 "Deleted all domains for $APP" + dokku_log_info1 "Cleared domains in $APP" } fn-domains-disable() { diff --git a/plugins/domains/subcommands/clear b/plugins/domains/subcommands/clear index 3b18beaa0..c6065abb2 100755 --- a/plugins/domains/subcommands/clear +++ b/plugins/domains/subcommands/clear @@ -5,7 +5,7 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/domains/functions" cmd-domains-clear() { - declare desc="remove all app domains" + declare desc="clear all app domains" declare cmd="domains:clear" [[ "$1" == "$cmd" ]] && shift 1 declare APP="$1" diff --git a/plugins/domains/subcommands/clear-global b/plugins/domains/subcommands/clear-global index 3fd4815b5..400450255 100755 --- a/plugins/domains/subcommands/clear-global +++ b/plugins/domains/subcommands/clear-global @@ -5,11 +5,11 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/domains/functions" cmd-domains-clear-global() { - declare desc="remove all global domains" + declare desc="clear global domain names" declare cmd="domains:clear-global" [[ "$1" == "$cmd" ]] && shift 1 - dokku_log_info1_quiet "Deleting global domains" + dokku_log_info1_quiet "Clearing global domains" domains_clear_global "$@" } From 77970a6b0d54284af36ac619ea08c7c783ea5e73 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:17:09 -0400 Subject: [PATCH 44/97] chore: revert doc change --- docs/configuration/domains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/domains.md b/docs/configuration/domains.md index f473fa6cd..120157f45 100644 --- a/docs/configuration/domains.md +++ b/docs/configuration/domains.md @@ -68,7 +68,7 @@ dokku domains:add node-js-app dokku.me # list custom domains for app dokku domains:report node-js-app -# delete all domains for app +# clear all custom domains for app dokku domains:clear node-js-app # remove a custom domain from app From b11c72d0ae9957b976a801acbfc16801bdddca41 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:17:57 -0400 Subject: [PATCH 45/97] tests: update test logic --- tests/unit/domains.bats | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/domains.bats b/tests/unit/domains.bats index b8fa384eb..362131020 100644 --- a/tests/unit/domains.bats +++ b/tests/unit/domains.bats @@ -347,8 +347,8 @@ teardown() { echo "status: $status" assert_success - # run domains:clear in order to invoke default vhost creation - run /bin/bash -c "dokku --quiet domains:clear test.dokku.test" + # run domains:reset in order to invoke default vhost creation + run /bin/bash -c "dokku --quiet domains:reset test.dokku.test" echo "output: $output" echo "status: $status" assert_success From ea6adbbf5685f0418924b926af582abdb2013bdc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:21:57 -0400 Subject: [PATCH 46/97] fix: add missing trigger and use helper function for clearing vhost --- plugins/domains/subcommands/reset | 6 ++---- plugins/domains/subcommands/setup | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/domains/subcommands/reset b/plugins/domains/subcommands/reset index 2136ef5a9..a01f95957 100755 --- a/plugins/domains/subcommands/reset +++ b/plugins/domains/subcommands/reset @@ -11,12 +11,10 @@ cmd-domains-reset() { declare APP="$1" verify_app_name "$APP" - local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" - - rm -f "$APP_VHOST_PATH" + fn-domains-clear "$APP" domains_setup "$APP" plugn trigger post-domains-update "$APP" "reset" - dokku_log_info1 "Reset domains for $APP" + dokku_log_info1_quiet "Reset domains for $APP" } cmd-domains-reset "$@" diff --git a/plugins/domains/subcommands/setup b/plugins/domains/subcommands/setup index 6458bf6c4..4d0bb19b3 100755 --- a/plugins/domains/subcommands/setup +++ b/plugins/domains/subcommands/setup @@ -12,6 +12,8 @@ cmd-domains-setup() { verify_app_name "$APP" domains_setup "$APP" + plugn trigger post-domains-update "$APP" "reset" + dokku_log_info1_quiet "Setup domains for $APP" } cmd-domains-setup "$@" From a2ac21130a19ae055dff467ada58e1a1db41cd8c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 01:22:05 -0400 Subject: [PATCH 47/97] docs: add migration note --- docs/appendices/0.36.0-migration-guide.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md index 2a79b6668..583fbfa75 100644 --- a/docs/appendices/0.36.0-migration-guide.md +++ b/docs/appendices/0.36.0-migration-guide.md @@ -3,4 +3,5 @@ ## Changes - Process stop timeouts are now configured via the `ps` property `stop-timeout-seconds`. Existing `DOKKU_DOCKER_STOP_TIMEOUT` environment variables will be automatically migrated to the new value. -- Processes now default to a `30` second stop timeout. \ No newline at end of file +- Processes now default to a `30` second stop timeout. +- The `domains:clear` command no longer runs domain setup after clearing the domains. Use `domains:reset` or run `domains:setup` manually to have the same effect. \ No newline at end of file From 8b07e92391f04edfa54f612040e8d3c477cba31c Mon Sep 17 00:00:00 2001 From: Naman gandhi Date: Wed, 25 Jun 2025 17:47:39 +0530 Subject: [PATCH 48/97] fix(registry): improve error handling in docker operations - Replace boolean return values with proper error handling in dockerTag and dockerPush functions - Add detailed error context including Docker command exit codes and stderr output - Remove TODO comments and improve error messages for better debugging - Fix error message formatting in defer function This addresses the TODO comments for better error handling and provides users with actionable error information when Docker registry operations fail. Closes: #XXXX --- plugins/registry/functions.go | 46 ++++++++++++++++++++--------------- plugins/registry/triggers.go | 6 ++--- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/plugins/registry/functions.go b/plugins/registry/functions.go index 7c2882c62..eb85e060b 100644 --- a/plugins/registry/functions.go +++ b/plugins/registry/functions.go @@ -2,7 +2,6 @@ package registry import ( "bytes" - "errors" "fmt" "strconv" "strings" @@ -96,14 +95,12 @@ func pushToRegistry(appName string, tag int, imageID string, imageRepo string) e fullImage := fmt.Sprintf("%s%s:%d", registryServer, imageRepo, tag) common.LogVerboseQuiet(fmt.Sprintf("Tagging %s:%d in registry format", imageRepo, tag)) - if !dockerTag(imageID, fullImage) { - // TODO: better error - return errors.New("Unable to tag image") + if err := dockerTag(imageID, fullImage); err != nil { + return fmt.Errorf("unable to tag image %s as %s: %w", imageID, fullImage, err) } - if !dockerTag(imageID, fmt.Sprintf("%s:%d", imageRepo, tag)) { - // TODO: better error - return errors.New("Unable to tag image") + if err := dockerTag(imageID, fmt.Sprintf("%s:%d", imageRepo, tag)); err != nil { + return fmt.Errorf("unable to tag image %s as %s:%d: %w", imageID, imageRepo, tag, err) } extraTags := getRegistryPushExtraTagsForApp(appName) @@ -112,26 +109,25 @@ func pushToRegistry(appName string, tag int, imageID string, imageRepo string) e for _, extraTag := range extraTagsArray { extraTagImage := fmt.Sprintf("%s%s:%s", registryServer, imageRepo, extraTag) common.LogVerboseQuiet(fmt.Sprintf("Tagging %s as %s in registry format", imageRepo, extraTag)) - if !dockerTag(imageID, extraTagImage) { - return errors.New(fmt.Sprintf("Unable to tag image as %s", extraTag)) + if err := dockerTag(imageID, extraTagImage); err != nil { + return fmt.Errorf("unable to tag image %s as %s: %w", imageID, extraTagImage, err) } defer func() { common.LogVerboseQuiet(fmt.Sprintf("Untagging extra tag %s", extraTag)) if err := common.RemoveImages([]string{extraTagImage}); err != nil { - common.LogWarn(fmt.Sprintf("Unable to untag extra tag %s", extraTag, err.Error())) + common.LogWarn(fmt.Sprintf("Unable to untag extra tag %s: %s", extraTag, err.Error())) } }() common.LogVerboseQuiet(fmt.Sprintf("Pushing %s", extraTagImage)) - if !dockerPush(extraTagImage) { - return errors.New(fmt.Sprintf("Unable to push image with %s tag", extraTag)) + if err := dockerPush(extraTagImage); err != nil { + return fmt.Errorf("unable to push image with %s tag: %w", extraTag, err) } } } common.LogVerboseQuiet(fmt.Sprintf("Pushing %s", fullImage)) - if !dockerPush(fullImage) { - // TODO: better error - return errors.New("Unable to push image") + if err := dockerPush(fullImage); err != nil { + return fmt.Errorf("unable to push image %s: %w", fullImage, err) } // Only clean up when the scheduler is not docker-local @@ -148,22 +144,34 @@ func pushToRegistry(appName string, tag int, imageID string, imageRepo string) e return nil } -func dockerTag(imageID string, imageTag string) bool { +func dockerTag(imageID string, imageTag string) error { result, err := common.CallExecCommand(common.ExecCommandInput{ Command: common.DockerBin(), Args: []string{"image", "tag", imageID, imageTag}, StreamStdio: true, }) - return err == nil && result.ExitCode == 0 + if err != nil { + return fmt.Errorf("docker tag command failed: %w", err) + } + if result.ExitCode != 0 { + return fmt.Errorf("docker tag command exited with code %d: %s", result.ExitCode, result.Stderr) + } + return nil } -func dockerPush(imageTag string) bool { +func dockerPush(imageTag string) error { result, err := common.CallExecCommand(common.ExecCommandInput{ Command: common.DockerBin(), Args: []string{"image", "push", imageTag}, StreamStdio: true, }) - return err == nil && result.ExitCode == 0 + if err != nil { + return fmt.Errorf("docker push command failed: %w", err) + } + if result.ExitCode != 0 { + return fmt.Errorf("docker push command exited with code %d: %s", result.ExitCode, result.Stderr) + } + return nil } func imageCleanup(appName string, imageRepo string, imageTag string, tag int) { diff --git a/plugins/registry/triggers.go b/plugins/registry/triggers.go index 44ec3d729..30cc97564 100644 --- a/plugins/registry/triggers.go +++ b/plugins/registry/triggers.go @@ -1,7 +1,6 @@ package registry import ( - "errors" "fmt" "strings" @@ -108,9 +107,8 @@ func TriggerPostReleaseBuilder(appName string, image string) error { newImage := strings.Replace(image, imageRepo+":", computedImageRepo+":", 1) if computedImageRepo != imageRepo { - if !dockerTag(imageID, newImage) { - // TODO: better error - return errors.New("Unable to tag image") + if err := dockerTag(imageID, newImage); err != nil { + return fmt.Errorf("unable to tag image %s as %s: %w", imageID, newImage, err) } } From 04b2643b4f0219cb1df74c651fcec51c0d50808c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 02:36:42 -0400 Subject: [PATCH 49/97] chore: remove ansi escape characters that dropped the remote: prefix in deploy output Closes #7728 --- docs/appendices/0.36.0-migration-guide.md | 3 ++- docs/configuration/environment-variables.md | 1 - plugins/git/functions | 11 +---------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md index 583fbfa75..508dd6b36 100644 --- a/docs/appendices/0.36.0-migration-guide.md +++ b/docs/appendices/0.36.0-migration-guide.md @@ -4,4 +4,5 @@ - Process stop timeouts are now configured via the `ps` property `stop-timeout-seconds`. Existing `DOKKU_DOCKER_STOP_TIMEOUT` environment variables will be automatically migrated to the new value. - Processes now default to a `30` second stop timeout. -- The `domains:clear` command no longer runs domain setup after clearing the domains. Use `domains:reset` or run `domains:setup` manually to have the same effect. \ No newline at end of file +- The `domains:clear` command no longer runs domain setup after clearing the domains. Use `domains:reset` or run `domains:setup` manually to have the same effect. +- The deprecated `DOKKU_DISABLE_ANSI_PREFIX_REMOVAL` setting has been removed. Dokku deploys no longer use shell escape codes to modify output, and thus deploy output may now contain `remote:` as a prefix in each lines output. diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index 047571c22..69da15c2d 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -110,7 +110,6 @@ The following config variables have special meanings and can be set in a variety | `DOKKU_CHECKS_ATTEMPTS` | `5` | `dokku config:set` | Number of retries for to run for a specific check before marking it as a failure | `DOKKU_DEFAULT_CHECKS_WAIT` | `10` | `dokku config:set` | If no user-defined checks are specified - or if the process being checked is not a `web` process - this is the period of time Dokku will wait before checking that a container is still running. | | `DOKKU_DISABLE_PROXY` | none | `dokku proxy:disable`
`dokku proxy:enable` | Disables the proxy in front of your application, resulting in publicly routing the docker container. | -| `DOKKU_DISABLE_ANSI_PREFIX_REMOVAL` | none | `dokku config:set`
`/etc/environment`
`~dokku/.dokkurc`
`~dokku/.dokkurc/*` | Disables removal of the ANSI prefix during deploys. Can be used in cases where the client deployer does not understand ansi escape codes. | | `DOKKU_DISABLE_APP_AUTOCREATION` | none | `dokku config:set` | Disables automatic creation of a non-existent app on deploy. | | `DOKKU_DOCKERFILE_CACHE_BUILD` | none | `dokku config:set` | | | `DOKKU_DOCKERFILE_START_CMD` | none | `dokku config:set` | | diff --git a/plugins/git/functions b/plugins/git/functions index 6e80a3f27..9de24288d 100755 --- a/plugins/git/functions +++ b/plugins/git/functions @@ -25,16 +25,7 @@ git_build_app_repo() { fn-git-setup-build-dir "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" >/dev/null - local DOKKU_APP_DISABLE_ANSI_PREFIX_REMOVAL DOKKU_GLOBAL_DISABLE_ANSI_PREFIX_REMOVAL DOKKU_DISABLE_ANSI_PREFIX_REMOVAL - DOKKU_APP_DISABLE_ANSI_PREFIX_REMOVAL=$(config_get "$APP" DOKKU_DISABLE_ANSI_PREFIX_REMOVAL || true) - DOKKU_GLOBAL_DISABLE_ANSI_PREFIX_REMOVAL=$(config_get --global DOKKU_DISABLE_ANSI_PREFIX_REMOVAL || true) - DOKKU_DISABLE_ANSI_PREFIX_REMOVAL=${DOKKU_APP_DISABLE_ANSI_PREFIX_REMOVAL:="$DOKKU_GLOBAL_DISABLE_ANSI_PREFIX_REMOVAL"} - - if [[ "$DOKKU_DISABLE_ANSI_PREFIX_REMOVAL" == "true" ]]; then - git_trigger_build "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" - else - git_trigger_build "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" | sed -u "s/^/"$'\e[1G'"/" - fi + git_trigger_build "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" return $? } From 6da9be2d65f6218000df188ce2bd2c5942822612 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 2 Jul 2025 02:20:39 -0400 Subject: [PATCH 50/97] refactor: use chart values directly instead of attempting to template out cron-job and keda files --- .../scheduler-k3s/templates/chart/cron-job.yaml | 8 ++++++-- .../scheduler-k3s/templates/chart/keda-secret.yaml | 2 +- .../chart/keda-trigger-authentication.yaml | 2 +- plugins/scheduler-k3s/triggers.go | 14 +++++++------- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/cron-job.yaml b/plugins/scheduler-k3s/templates/chart/cron-job.yaml index 412e57916..1a04c31d2 100644 --- a/plugins/scheduler-k3s/templates/chart/cron-job.yaml +++ b/plugins/scheduler-k3s/templates/chart/cron-job.yaml @@ -1,5 +1,8 @@ -{{- $processName := "CRON_ID" }} -{{- $config := index .Values.processes "CRON_ID" }} +{{- $processName, $config := range .Values.processes }} +# Only process cron jobs +{{- if not (and (hasKey $config "cron") (hasKey $config.cron "id")) }} +{{- continue }} +{{- end }} --- apiVersion: batch/v1 kind: CronJob @@ -113,3 +116,4 @@ spec: successfulJobsHistoryLimit: 10 suspend: false timeZone: Etc/UTC +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml index 3e40bcc62..83177b2c8 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml @@ -1,5 +1,5 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} -{{- $config := index .Values.global.keda.authentications "AUTH_NAME" }} +{{- $authName, $config := range .Values.global.keda.authentications }} --- apiVersion: v1 kind: Secret diff --git a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml index c39b94b0a..fc40fa6be 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml @@ -1,5 +1,5 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} -{{- $config := index .Values.global.keda.authentications "AUTH_NAME" }} +{{- $authName, $config := range .Values.global.keda.authentications }} --- apiVersion: keda.sh/v1alpha1 kind: TriggerAuthentication diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index d81094cbe..89827cdeb 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -368,7 +368,7 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e Processes: map[string]ProcessValues{}, } - for authName := range kedaValues.Authentications { + if len(kedaValues.Authentications) > 0 { templateFiles := []string{"keda-secret", "keda-trigger-authentication"} for _, templateName := range templateFiles { b, err := templates.ReadFile(fmt.Sprintf("templates/chart/%s.yaml", templateName)) @@ -376,9 +376,8 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e return fmt.Errorf("Error reading %s template: %w", templateName, err) } - filename := filepath.Join(chartDir, "templates", fmt.Sprintf("%s-%s.yaml", templateName, authName)) - contents := strings.ReplaceAll(string(b), "AUTH_NAME", authName) - err = os.WriteFile(filename, []byte(contents), os.FileMode(0644)) + filename := filepath.Join(chartDir, "templates", fmt.Sprintf("%s.yaml", templateName)) + err = os.WriteFile(filename, b, os.FileMode(0644)) if err != nil { return fmt.Errorf("Error writing %s template: %w", templateName, err) } @@ -613,15 +612,16 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e Volumes: processVolumes, } values.Processes[cronEntry.ID] = processValues + } + if len(cronEntries) > 0 { b, err := templates.ReadFile("templates/chart/cron-job.yaml") if err != nil { return fmt.Errorf("Error reading cron job template: %w", err) } - cronFile := filepath.Join(chartDir, "templates", fmt.Sprintf("cron-job-%s.yaml", cronEntry.ID)) - contents := strings.ReplaceAll(string(b), "CRON_ID", cronEntry.ID) - err = os.WriteFile(cronFile, []byte(contents), os.FileMode(0644)) + cronFile := filepath.Join(chartDir, "templates", "cron-job.yaml") + err = os.WriteFile(cronFile, b, os.FileMode(0644)) if err != nil { return fmt.Errorf("Error writing cron job template: %w", err) } From 1b95e01ea378631d2da1ada5ef8d0f9ed8442bc3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 01:46:03 -0400 Subject: [PATCH 51/97] fix: move range --- plugins/scheduler-k3s/templates/chart/cron-job.yaml | 2 +- plugins/scheduler-k3s/templates/chart/deployment.yaml | 9 +++++++-- plugins/scheduler-k3s/templates/chart/keda-secret.yaml | 2 +- .../templates/chart/keda-trigger-authentication.yaml | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/cron-job.yaml b/plugins/scheduler-k3s/templates/chart/cron-job.yaml index 1a04c31d2..cad22ce7c 100644 --- a/plugins/scheduler-k3s/templates/chart/cron-job.yaml +++ b/plugins/scheduler-k3s/templates/chart/cron-job.yaml @@ -1,4 +1,4 @@ -{{- $processName, $config := range .Values.processes }} +{{- range $processName, $config := .Values.processes }} # Only process cron jobs {{- if not (and (hasKey $config "cron") (hasKey $config.cron "id")) }} {{- continue }} diff --git a/plugins/scheduler-k3s/templates/chart/deployment.yaml b/plugins/scheduler-k3s/templates/chart/deployment.yaml index 3bb1d6e64..6d06ec008 100644 --- a/plugins/scheduler-k3s/templates/chart/deployment.yaml +++ b/plugins/scheduler-k3s/templates/chart/deployment.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- $mappings := dict }} {{- if eq $processName "web" }} {{- range $pdx, $port_map := $config.web.port_maps }} @@ -143,3 +147,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml index 83177b2c8..da27a08cc 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml @@ -1,5 +1,5 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} -{{- $authName, $config := range .Values.global.keda.authentications }} +{{- range $authName, $config := .Values.global.keda.authentications }} --- apiVersion: v1 kind: Secret diff --git a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml index fc40fa6be..2abf309cd 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml @@ -1,5 +1,5 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} -{{- $authName, $config := range .Values.global.keda.authentications }} +{{- range $authName, $config := .Values.global.keda.authentications }} --- apiVersion: keda.sh/v1alpha1 kind: TriggerAuthentication From c472c2a3c70f91762083132762bd04e829960385 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 02:04:45 -0400 Subject: [PATCH 52/97] refactor use chart values directly instead of attempting to template out cron-job and keda files --- plugins/scheduler-k3s/templates/chart/certificate.yaml | 9 +++++++-- .../templates/chart/https-redirect-middleware.yaml | 9 +++++++-- plugins/scheduler-k3s/templates/chart/ingress-route.yaml | 9 +++++++-- plugins/scheduler-k3s/templates/chart/ingress.yaml | 9 +++++++-- .../templates/chart/keda-http-scaled-object.yaml | 9 +++++++-- .../templates/chart/keda-interceptor-proxy-service.yaml | 9 +++++++-- .../templates/chart/keda-scaled-object.yaml | 9 +++++++-- plugins/scheduler-k3s/templates/chart/service.yaml | 9 +++++++-- plugins/scheduler-k3s/triggers.go | 5 ++--- 9 files changed, 58 insertions(+), 19 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/certificate.yaml b/plugins/scheduler-k3s/templates/chart/certificate.yaml index 029230020..a16e03a8a 100644 --- a/plugins/scheduler-k3s/templates/chart/certificate.yaml +++ b/plugins/scheduler-k3s/templates/chart/certificate.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and $config.web.tls.enabled $config.web.domains }} --- apiVersion: cert-manager.io/v1 @@ -34,3 +38,4 @@ spec: - {{ $domain.name }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml index 20a3d4429..d81397a53 100644 --- a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml +++ b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and $config.web.domains (eq $.Values.global.network.ingress_class "traefik") }} --- apiVersion: traefik.io/v1alpha1 @@ -22,3 +26,4 @@ spec: scheme: https permanent: true {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml index 306e4a7a3..01bf6ff76 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and $config.web.domains (eq $.Values.global.network.ingress_class "traefik") }} {{- $mappings := dict }} {{- range $pdx, $port_map := $config.web.port_maps }} @@ -53,3 +57,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress.yaml b/plugins/scheduler-k3s/templates/chart/ingress.yaml index 5530525f3..d21247034 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- $routeToKeda := false }} {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} {{- if and (.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} @@ -59,3 +63,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml index bd761642f..e4d4fdd08 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} {{- if and (.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} {{- $mappings := dict }} @@ -51,3 +55,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml index c12d6411e..ed4575b93 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} {{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} --- @@ -24,3 +28,4 @@ spec: externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml index 24e6ed5e1..9970b1b31 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + {{- if and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda")) }} {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} --- @@ -42,3 +46,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/service.yaml b/plugins/scheduler-k3s/templates/chart/service.yaml index bf5d3d34b..0b6a02417 100644 --- a/plugins/scheduler-k3s/templates/chart/service.yaml +++ b/plugins/scheduler-k3s/templates/chart/service.yaml @@ -1,5 +1,9 @@ -{{- $processName := "PROCESS_NAME" }} -{{- $config := index .Values.processes "PROCESS_NAME" }} +{{- range $processName, $config := .Values.processes }} +# Skip cron jobs +{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +{{- continue }} +{{- end }} + --- apiVersion: v1 kind: Service @@ -28,3 +32,4 @@ spec: app.kubernetes.io/instance: {{ $.Values.global.app_name }}-{{ $processName }} app.kubernetes.io/name: {{ $processName }} app.kubernetes.io/part-of: {{ $.Values.global.app_name }} +{{- end }} diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 89827cdeb..a228ac048 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -534,9 +534,8 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e return fmt.Errorf("Error reading %s template: %w", templateName, err) } - filename := filepath.Join(chartDir, "templates", fmt.Sprintf("%s-%s.yaml", templateName, processType)) - contents := strings.ReplaceAll(string(b), "PROCESS_NAME", processType) - err = os.WriteFile(filename, []byte(contents), os.FileMode(0644)) + filename := filepath.Join(chartDir, "templates", fmt.Sprintf("%s.yaml", templateName)) + err = os.WriteFile(filename, b, os.FileMode(0644)) if err != nil { return fmt.Errorf("Error writing %s template: %w", templateName, err) } From f7800adb68b101dc5fd92521cd0ef78ce75e0a46 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 02:46:23 -0400 Subject: [PATCH 53/97] fix: correct scope for capability checks --- .../templates/chart/keda-http-scaled-object.yaml | 4 +++- .../templates/chart/keda-interceptor-proxy-service.yaml | 2 +- plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml index e4d4fdd08..be6890a96 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml @@ -1,3 +1,4 @@ +{{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} # Skip cron jobs {{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} @@ -5,7 +6,7 @@ {{- end }} {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} -{{- if and (.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} +{{- if and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http") }} {{- $mappings := dict }} {{- range $pdx, $port_map := $config.web.port_maps }} {{- $mappings := set $mappings $port_map.name "true" }} @@ -56,3 +57,4 @@ spec: {{- end }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml index ed4575b93..4c259c946 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml @@ -1,3 +1,4 @@ +{{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} # Skip cron jobs {{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} @@ -5,7 +6,6 @@ {{- end }} {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} -{{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} --- kind: Service apiVersion: v1 diff --git a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml index 9970b1b31..b6633556f 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml @@ -1,3 +1,4 @@ +{{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} # Skip cron jobs {{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} @@ -5,7 +6,6 @@ {{- end }} {{- if and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda")) }} -{{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} --- apiVersion: keda.sh/v1alpha1 kind: ScaledObject From 27dbae3703e34ba64bcf0eebcd8acff915782b40 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 04:24:04 -0400 Subject: [PATCH 54/97] fix: correct scope for capability checks --- plugins/scheduler-k3s/templates/chart/ingress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/templates/chart/ingress.yaml b/plugins/scheduler-k3s/templates/chart/ingress.yaml index d21247034..b416c3eab 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress.yaml @@ -6,7 +6,7 @@ {{- $routeToKeda := false }} {{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} -{{- if and (.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} +{{- if and ($.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} {{- $routeToKeda = true }} {{- end }} {{- end }} From 4a891f74bd1b2e2ca36c4eb2f313a5f188d6bd58 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 4 Jul 2025 06:09:03 -0400 Subject: [PATCH 55/97] fix: correct scope for capability checks --- plugins/scheduler-k3s/templates/chart/keda-secret.yaml | 1 + .../templates/chart/keda-trigger-authentication.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml index da27a08cc..e8a3b7aa1 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-secret.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-secret.yaml @@ -21,3 +21,4 @@ data: {{- toYaml . | nindent 2 }} {{- end }} {{- end }} +{{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml index 2abf309cd..bf77542be 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-trigger-authentication.yaml @@ -26,3 +26,4 @@ spec: parameter: {{ $k }} {{- end }} {{- end }} +{{- end }} From 3cc29ff0162129bb371b8443f3b6b928ec06c338 Mon Sep 17 00:00:00 2001 From: Alessandro Rodi Date: Tue, 8 Jul 2025 14:01:03 +0200 Subject: [PATCH 56/97] Update 0.36.0-migration-guide.md --- docs/appendices/0.36.0-migration-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md index 508dd6b36..54f9b17eb 100644 --- a/docs/appendices/0.36.0-migration-guide.md +++ b/docs/appendices/0.36.0-migration-guide.md @@ -1,4 +1,4 @@ -# 0.35.0 Migration Guide +# 0.36.0 Migration Guide ## Changes From 93f12c59e68c41b69dbf7bebb0bd05f2b01d64e0 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 21:14:33 -0400 Subject: [PATCH 57/97] feat: update golang to 1.24 --- common.mk | 2 +- go.work | 2 +- plugins/app-json/go.mod | 2 +- plugins/apps/go.mod | 2 +- plugins/builder/go.mod | 2 +- plugins/buildpacks/go.mod | 2 +- plugins/common/go.mod | 2 +- plugins/config/go.mod | 2 +- plugins/cron/go.mod | 2 +- plugins/docker-options/go.mod | 2 +- plugins/logs/go.mod | 2 +- plugins/network/go.mod | 2 +- plugins/nginx-vhosts/go.mod | 2 +- plugins/ports/go.mod | 2 +- plugins/proxy/go.mod | 2 +- plugins/ps/go.mod | 2 +- plugins/registry/go.mod | 2 +- plugins/repo/go.mod | 2 +- plugins/resource/go.mod | 2 +- plugins/scheduler-docker-local/go.mod | 2 +- plugins/scheduler-k3s/go.mod | 2 +- plugins/scheduler/go.mod | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/common.mk b/common.mk index d6a00a5a6..450e9688d 100644 --- a/common.mk +++ b/common.mk @@ -1,7 +1,7 @@ GO_ARGS ?= GO_PLUGIN_MAKE_TARGET ?= build GO_REPO_ROOT := /go/src/github.com/dokku/dokku -BUILD_IMAGE := golang:1.23.0 +BUILD_IMAGE := golang:1.24.5 GO_BUILD_CACHE ?= /tmp/dokku-go-build-cache GO_MOD_CACHE ?= /tmp/dokku-go-mod-mod GO_ROOT_MOUNT ?= $$PWD/../..:$(GO_REPO_ROOT) diff --git a/go.work b/go.work index 72ea6651e..a7a0ee40c 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.23.0 +go 1.24.0 use ( ./plugins/app-json diff --git a/plugins/app-json/go.mod b/plugins/app-json/go.mod index 9116a51ca..b70b63489 100644 --- a/plugins/app-json/go.mod +++ b/plugins/app-json/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/app-json -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/apps/go.mod b/plugins/apps/go.mod index ebaad4008..935537634 100644 --- a/plugins/apps/go.mod +++ b/plugins/apps/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/apps -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/builder/go.mod b/plugins/builder/go.mod index ff3840fa0..d46541ca6 100644 --- a/plugins/builder/go.mod +++ b/plugins/builder/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/builder -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/buildpacks/go.mod b/plugins/buildpacks/go.mod index 44e15bddb..57ac12969 100644 --- a/plugins/buildpacks/go.mod +++ b/plugins/buildpacks/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/buildpacks -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/common/go.mod b/plugins/common/go.mod index fc2372707..f0177deb3 100644 --- a/plugins/common/go.mod +++ b/plugins/common/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/common -go 1.23.0 +go 1.24.0 toolchain go1.24.1 diff --git a/plugins/config/go.mod b/plugins/config/go.mod index f810332fc..c4566f2e5 100644 --- a/plugins/config/go.mod +++ b/plugins/config/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/config -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/cron/go.mod b/plugins/cron/go.mod index 09ec4c2e4..d4170a313 100644 --- a/plugins/cron/go.mod +++ b/plugins/cron/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/cron -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 diff --git a/plugins/docker-options/go.mod b/plugins/docker-options/go.mod index 74045ac5e..5ec40541e 100644 --- a/plugins/docker-options/go.mod +++ b/plugins/docker-options/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/docker-options -go 1.23.0 +go 1.24.0 require github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/logs/go.mod b/plugins/logs/go.mod index c3379087c..5304a8fca 100644 --- a/plugins/logs/go.mod +++ b/plugins/logs/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/logs -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/network/go.mod b/plugins/network/go.mod index 89f9dd2f1..9a9df9d64 100644 --- a/plugins/network/go.mod +++ b/plugins/network/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/network -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/nginx-vhosts/go.mod b/plugins/nginx-vhosts/go.mod index 3897cb106..6fffca170 100644 --- a/plugins/nginx-vhosts/go.mod +++ b/plugins/nginx-vhosts/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/nginx-vhosts -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/ports/go.mod b/plugins/ports/go.mod index e9de9ed08..1e04b6fae 100644 --- a/plugins/ports/go.mod +++ b/plugins/ports/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/ports -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/proxy/go.mod b/plugins/proxy/go.mod index d577e388b..89bf527ee 100644 --- a/plugins/proxy/go.mod +++ b/plugins/proxy/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/proxy -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/ps/go.mod b/plugins/ps/go.mod index 70d245401..7cb1b6598 100644 --- a/plugins/ps/go.mod +++ b/plugins/ps/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/ps -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/registry/go.mod b/plugins/registry/go.mod index 0aace5cc7..244b07925 100644 --- a/plugins/registry/go.mod +++ b/plugins/registry/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/registry -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/repo/go.mod b/plugins/repo/go.mod index d82f384bb..1f07d43f3 100644 --- a/plugins/repo/go.mod +++ b/plugins/repo/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/repo -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/resource/go.mod b/plugins/resource/go.mod index e7ea4f721..8453760a2 100644 --- a/plugins/resource/go.mod +++ b/plugins/resource/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/resource -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/scheduler-docker-local/go.mod b/plugins/scheduler-docker-local/go.mod index 612775e50..6af90f0eb 100644 --- a/plugins/scheduler-docker-local/go.mod +++ b/plugins/scheduler-docker-local/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/scheduler-docker-local -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 diff --git a/plugins/scheduler-k3s/go.mod b/plugins/scheduler-k3s/go.mod index 152503419..81d404136 100644 --- a/plugins/scheduler-k3s/go.mod +++ b/plugins/scheduler-k3s/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/scheduler-k3s -go 1.23.0 +go 1.24.0 require ( github.com/cert-manager/cert-manager v1.15.4 diff --git a/plugins/scheduler/go.mod b/plugins/scheduler/go.mod index c5742cd6b..3312ef412 100644 --- a/plugins/scheduler/go.mod +++ b/plugins/scheduler/go.mod @@ -1,6 +1,6 @@ module github.com/dokku/dokku/plugins/scheduler -go 1.23.0 +go 1.24.0 require ( github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 From 2c30c03ef7e27139a06d1c4b905d1ca6738adc5d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 21:28:57 -0400 Subject: [PATCH 58/97] fix: non-constant format string in call to fmt.Printf --- plugins/common/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/common/common.go b/plugins/common/common.go index 80aed6746..5a101831a 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -578,7 +578,7 @@ func SuppressOutput(f errfunc) error { os.Stdout = rescueStdout if err != nil { - fmt.Printf(string(out[:])) + fmt.Print(string(out[:])) } return err From 27b5fd93fd5781a396452f4ba4edc8f9b115a72a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 22:31:46 -0400 Subject: [PATCH 59/97] feat: export the devcontainer environment to allow docker exec to work This will allow Dokku developers access to the devcontainer on their preferred terminal vs just the built-in one in VSCode. --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7b1d82267..f0a2a7825 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,9 +31,10 @@ ], "overrideCommand": false, "postCreateCommand": "setup-dev-env", + "postStartCommand": "printenv > .vscode/devcontainer.env", "runArgs": [ "--init" ], "workspaceFolder": "/root/go/src/github.com/dokku/dokku", "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/root/go/src/github.com/dokku/dokku,consistency=cached" -} +} \ No newline at end of file From 2d4039ab698302bc287bbae3936925059c97ba2d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 22:34:24 -0400 Subject: [PATCH 60/97] chore: update gitignore to remove compiled triggers from repo --- plugins/cron/.gitignore | 2 ++ plugins/scheduler-docker-local/.gitignore | 1 + 2 files changed, 3 insertions(+) diff --git a/plugins/cron/.gitignore b/plugins/cron/.gitignore index 949e5b1cd..ee795a9c9 100644 --- a/plugins/cron/.gitignore +++ b/plugins/cron/.gitignore @@ -2,7 +2,9 @@ /subcommands/* /triggers/* /triggers +/app-json-is-valid /cron-* /install /post-* /report +/scheduler-stop diff --git a/plugins/scheduler-docker-local/.gitignore b/plugins/scheduler-docker-local/.gitignore index 637119bb8..cdf10f66f 100644 --- a/plugins/scheduler-docker-local/.gitignore +++ b/plugins/scheduler-docker-local/.gitignore @@ -1,3 +1,4 @@ /triggers/* /triggers /cron-* +/scheduler-cron-write From 83de73ccdb783285cabeb0ef69fbeefea8b04aa3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 22:56:48 -0400 Subject: [PATCH 61/97] chore(deps): update golang dependencies in scheduler-k3s plugin --- plugins/scheduler-k3s/go.mod | 199 ++++++----- plugins/scheduler-k3s/go.sum | 623 +++++++++++++++++------------------ 2 files changed, 397 insertions(+), 425 deletions(-) diff --git a/plugins/scheduler-k3s/go.mod b/plugins/scheduler-k3s/go.mod index 81d404136..2e7f2df80 100644 --- a/plugins/scheduler-k3s/go.mod +++ b/plugins/scheduler-k3s/go.mod @@ -3,122 +3,111 @@ module github.com/dokku/dokku/plugins/scheduler-k3s go 1.24.0 require ( - github.com/cert-manager/cert-manager v1.15.4 - github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/cron v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/logs v0.0.0-00010101000000-000000000000 - github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-00010101000000-000000000000 + github.com/cert-manager/cert-manager v1.18.2 + github.com/dokku/dokku/plugins/app-json v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/common v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/config v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/cron v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/logs v0.0.0-20250618161309-8d0c35f1333c + github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-20250618161309-8d0c35f1333c github.com/fatih/color v1.18.0 - github.com/go-openapi/jsonpointer v0.21.0 + github.com/go-openapi/jsonpointer v0.21.1 github.com/go-resty/resty/v2 v2.16.5 github.com/gofrs/flock v0.12.1 - github.com/gosimple/slug v1.14.0 + github.com/gosimple/slug v1.15.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/kedacore/keda/v2 v2.13.0 + github.com/kedacore/keda/v2 v2.17.1-0.20250708210620-a239d2459a35 github.com/ryanuber/columnize v2.1.2+incompatible github.com/spf13/pflag v1.0.6 - github.com/traefik/traefik/v2 v2.11.25 + github.com/traefik/traefik/v2 v2.11.26 golang.org/x/sync v0.15.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.14.2 - k8s.io/api v0.30.1 - k8s.io/apimachinery v0.30.1 - k8s.io/client-go v0.30.1 - k8s.io/kubectl v0.29.0 - k8s.io/kubernetes v1.29.14 - k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 - mvdan.cc/sh/v3 v3.11.0 + helm.sh/helm/v3 v3.18.4 + k8s.io/api v0.33.2 + k8s.io/apimachinery v0.33.2 + k8s.io/client-go v0.33.2 + k8s.io/kubectl v0.33.2 + k8s.io/kubernetes v1.33.2 + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 + mvdan.cc/sh/v3 v3.12.0 ) require ( - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/alexellis/go-execute/v2 v2.2.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/carapace-sh/carapace-shlex v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/chai2010/gettext-go v1.0.3 // indirect github.com/containerd/containerd v1.7.27 // indirect - github.com/containerd/errdefs v0.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v27.1.1+incompatible // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v27.1.1+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-metrics v0.0.1 // indirect - github.com/dokku/dokku/plugins/docker-options v0.0.0-00010101000000-000000000000 // indirect - github.com/emicklei/go-restful/v3 v3.12.0 // indirect - github.com/evanphx/json-patch v5.9.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect - github.com/expr-lang/expr v1.17.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/go-acme/lego/v4 v4.23.1 // indirect + github.com/docker/docker-credential-helpers v0.9.3 // indirect + github.com/dokku/dokku/plugins/docker-options v0.0.0-20250618161309-8d0c35f1333c // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/evanphx/json-patch v5.9.11+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/expr-lang/expr v1.17.5 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/go-acme/lego/v4 v4.24.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.5 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-jose/go-jose/v4 v4.1.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/websocket v1.5.3 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/jmoiron/sqlx v1.4.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/joncalhoun/qson v0.0.0-20200422171543-84433dcd3da0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/melbahja/goph v1.4.0 // indirect - github.com/miekg/dns v1.1.64 // indirect + github.com/miekg/dns v1.1.66 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/locker v1.0.1 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.5.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -126,68 +115,64 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pkg/sftp v1.13.5 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/pkg/sftp v1.13.9 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/rubenv/sql-migrate v1.8.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/cast v1.9.2 // indirect + github.com/spf13/cobra v1.9.1 // indirect github.com/traefik/paerser v0.2.2 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect - go.starlark.net v0.0.0-20231121155337-90ade8b19d09 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect golang.org/x/mod v0.25.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.26.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/grpc v1.71.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/evanphx/json-patch.v5 v5.8.1 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.34.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + google.golang.org/grpc v1.73.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.30.1 // indirect - k8s.io/apiserver v0.30.1 // indirect - k8s.io/cli-runtime v0.29.0 // indirect - k8s.io/component-base v0.30.1 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect - knative.dev/pkg v0.0.0-20240116073220-b488e7be5902 // indirect - oras.land/oras-go v1.2.5 // indirect - sigs.k8s.io/controller-runtime v0.18.2 // indirect - sigs.k8s.io/gateway-api v1.1.0 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.16.0 // indirect - sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/apiextensions-apiserver v0.33.2 // indirect + k8s.io/apiserver v0.33.2 // indirect + k8s.io/cli-runtime v0.33.2 // indirect + k8s.io/component-base v0.33.2 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 // indirect + knative.dev/pkg v0.0.0-20250708013613-d3550d4350f9 // indirect + oras.land/oras-go/v2 v2.6.0 // indirect + sigs.k8s.io/controller-runtime v0.21.0 // indirect + sigs.k8s.io/gateway-api v1.3.0 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/kustomize/api v0.20.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.20.0 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/yaml v1.5.0 // indirect ) replace github.com/dokku/dokku/plugins/app-json => ../app-json @@ -205,3 +190,5 @@ replace github.com/dokku/dokku/plugins/logs => ../logs replace github.com/dokku/dokku/plugins/nginx-vhosts => ../nginx-vhosts replace github.com/joho/godotenv => github.com/joho/godotenv v1.2.0 + +replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 diff --git a/plugins/scheduler-k3s/go.sum b/plugins/scheduler-k3s/go.sum index beb819ffd..46f0a044d 100644 --- a/plugins/scheduler-k3s/go.sum +++ b/plugins/scheduler-k3s/go.sum @@ -1,308 +1,246 @@ -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= -github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexellis/go-execute/v2 v2.2.1 h1:4Ye3jiCKQarstODOEmqDSRCqxMHLkC92Bhse743RdOI= github.com/alexellis/go-execute/v2 v2.2.1/go.mod h1:FMdRnUTiFAmYXcv23txrp3VYZfLo24nMpiIneWgKHTQ= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/carapace-sh/carapace-shlex v1.0.1 h1:ww0JCgWpOVuqWG7k3724pJ18Lq8gh5pHQs9j3ojUs1c= +github.com/carapace-sh/carapace-shlex v1.0.1/go.mod h1:lJ4ZsdxytE0wHJ8Ta9S7Qq0XpjgjU0mdfCqiI2FHx7M= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cert-manager/cert-manager v1.15.4 h1:FtH6BOTmkNBNRjoYSW2b80MYpUq4Zw1zbEB6flYzkiM= -github.com/cert-manager/cert-manager v1.15.4/go.mod h1:stBge/DTvrhfQMB/93+Y62s+gQgZBsfL1o0C/4AL/mI= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cert-manager/cert-manager v1.18.2 h1:H2P75ycGcTMauV3gvpkDqLdS3RSXonWF2S49QGA1PZE= +github.com/cert-manager/cert-manager v1.18.2/go.mod h1:icDJx4kG9BCNpGjBvrmsFd99d+lXUvWdkkcrSSQdIiw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= -github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= +github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= -github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= -github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= -github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= -github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= -github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM= +github.com/distribution/distribution/v3 v3.0.0/go.mod h1:tRNuFoZsUdyRVegq8xGNeds4KLjwLCRin/tTo6i1DhU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= -github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= -github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= -github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/expr-lang/expr v1.17.0 h1:+vpszOyzKLQXC9VF+wA8cVA0tlA984/Wabc/1hF9Whg= -github.com/expr-lang/expr v1.17.0/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= +github.com/expr-lang/expr v1.17.5 h1:i1WrMvcdLF249nSNlpQZN1S6NXuW9WaOfF5tPi3aw3k= +github.com/expr-lang/expr v1.17.5/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= -github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4= -github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-acme/lego/v4 v4.24.0 h1:pe0q49JKxfSGEP3lkgkMVQrZM1KbD+e0dpJ2McYsiVw= +github.com/go-acme/lego/v4 v4.24.0/go.mod h1:hkstZY6D0jylIrZbuNmEQrWQxTIfaJH7prwaWvKDOjw= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= -github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI= +github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= +github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= -github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= -github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= -github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= -github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= -github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es= -github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= +github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= -github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= +github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= +github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= +github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc= github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joncalhoun/qson v0.0.0-20200422171543-84433dcd3da0 h1:ct2XA1aDw8A07Dr8gtrrZgIgLKcZNAl2o9nn0WRMK4Y= github.com/joncalhoun/qson v0.0.0-20200422171543-84433dcd3da0/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= -github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kedacore/keda/v2 v2.13.0 h1:vUjLnEUQGrPBavyEFbPK7KxG10w+HfOnWk3eg8NPnnY= -github.com/kedacore/keda/v2 v2.13.0/go.mod h1:AZTRgxWpK5/6pq+DqJ15y3Bl/C8sl9C7tUVF4phzGDQ= +github.com/kedacore/keda/v2 v2.17.1-0.20250708210620-a239d2459a35 h1:dVamAZ0mkRedF4AMZDFoaV1lakRfdfrzN9tvk+zgAlk= +github.com/kedacore/keda/v2 v2.17.1-0.20250708210620-a239d2459a35/go.mod h1:WGhOV69k9YW3iTSj592glrCOqdO9Zy8hmYcyStZ5+tk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a h1:cwHOqPB4H4iQq8177kf2SxpjNbcjJ2m3lNwKIe28Hqg= -github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= -github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= -github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= -github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= -github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs= github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68= -github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= -github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= -github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= @@ -311,18 +249,17 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= @@ -333,77 +270,73 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= +github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw= +github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= -github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2Ns0o= +github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= +github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/traefik/paerser v0.2.2 h1:cpzW/ZrQrBh3mdwD/jnp6aXASiUFKOVr6ldP+keJTcQ= github.com/traefik/paerser v0.2.2/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24= -github.com/traefik/traefik/v2 v2.11.25 h1:gkWvq7zhnLqJJlivFfnITWiDwTmph2EQt1XI8pWCnKI= -github.com/traefik/traefik/v2 v2.11.25/go.mod h1:OFifHrTIFq/UUHvtXtSrNSAxqA6Cgmh+Gcq7eiGC4iA= +github.com/traefik/traefik/v2 v2.11.26 h1:i+kjoyL8lwe4E7lLtin/+IhFz0zXkKwAsmDFaMOBmQA= +github.com/traefik/traefik/v2 v2.11.26/go.mod h1:VuRYNzcBHrI8fan5VJGlhgSI1NCmL8Ma4HDjT6rsTKo= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -416,77 +349,114 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.starlark.net v0.0.0-20231121155337-90ade8b19d09 h1:hzy3LFnSN8kuQK8h9tHl4ndF6UruMj47OqwqsS+/Ai4= -go.starlark.net v0.0.0-20231121155337-90ade8b19d09/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= +go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= +go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= +go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= +go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA= +go.opentelemetry.io/otel/exporters/prometheus v0.59.0 h1:HHf+wKS6o5++XZhS98wvILrLVgHxjA/AMjqHKes+uzo= +go.opentelemetry.io/otel/exporters/prometheus v0.59.0/go.mod h1:R8GpRXTZrqvXHDEGVH5bF6+JqAZcK8PjJcZ5nGhEWiE= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0/go.mod h1:tx8OOlGH6R4kLV67YaYO44GFXloEjGPZuMjEkaaqIp4= +go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= +go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= +go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -496,109 +466,124 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v5 v5.8.1 h1:BVxXj2YS+4i9fttNkVvDKi4Pg1pVMpVE8tdEwaKeQY0= -gopkg.in/evanphx/json-patch.v5 v5.8.1/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -helm.sh/helm/v3 v3.14.2 h1:V71fv+NGZv0icBlr+in1MJXuUIHCiPG1hW9gEBISTIA= -helm.sh/helm/v3 v3.14.2/go.mod h1:2itvvDv2WSZXTllknfQo6j7u3VVgMAvm8POCDgYH424= -k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= -k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= -k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws= -k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4= -k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= -k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/apiserver v0.30.1 h1:BEWEe8bzS12nMtDKXzCF5Q5ovp6LjjYkSp8qOPk8LZ8= -k8s.io/apiserver v0.30.1/go.mod h1:i87ZnQ+/PGAmSbD/iEKM68bm1D5reX8fO4Ito4B01mo= -k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4= -k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk= -k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q= -k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc= -k8s.io/component-base v0.30.1 h1:bvAtlPh1UrdaZL20D9+sWxsJljMi0QZ3Lmw+kmZAaxQ= -k8s.io/component-base v0.30.1/go.mod h1:e/X9kDiOebwlI41AvBHuWdqFriSRrX50CdwA9TFaHLI= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM= -k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro= -k8s.io/kubectl v0.29.0 h1:Oqi48gXjikDhrBF67AYuZRTcJV4lg2l42GmvsP7FmYI= -k8s.io/kubectl v0.29.0/go.mod h1:0jMjGWIcMIQzmUaMgAzhSELv5WtHo2a8pq67DtviAJs= -k8s.io/kubernetes v1.29.14 h1:mbk5M/WaJr/AX7p3p2/7eJjrtI+I3AKu7IEjI8+Ny/U= -k8s.io/kubernetes v1.29.14/go.mod h1:L6/pfKQZ6Tv2O8gyT4OxhGZp+nNsjV54xtNodRoup9k= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -knative.dev/pkg v0.0.0-20240116073220-b488e7be5902 h1:H6+JJN23fhwYWCHY1339sY6uhIyoUwDy1a8dN233fdk= -knative.dev/pkg v0.0.0-20240116073220-b488e7be5902/go.mod h1:NYk8mMYoLkO7CQWnNkti4YGGnvLxN6MIDbUvtgeo0C0= -mvdan.cc/sh/v3 v3.11.0 h1:q5h+XMDRfUGUedCqFFsjoFjrhwf2Mvtt1rkMvVz0blw= -mvdan.cc/sh/v3 v3.11.0/go.mod h1:LRM+1NjoYCzuq/WZ6y44x14YNAI0NK7FLPeQSaFagGg= -oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= -oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= -sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= -sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= -sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM= -sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.16.0 h1:/zAR4FOQDCkgSDmVzV2uiFbuy9bhu3jEzthrHCuvm1g= -sigs.k8s.io/kustomize/api v0.16.0/go.mod h1:MnFZ7IP2YqVyVwMWoRxPtgl/5hpA+eCCrQR/866cm5c= -sigs.k8s.io/kustomize/kyaml v0.16.0 h1:6J33uKSoATlKZH16unr2XOhDI+otoe2sR3M8PDzW3K0= -sigs.k8s.io/kustomize/kyaml v0.16.0/go.mod h1:xOK/7i+vmE14N2FdFyugIshB8eF6ALpy7jI87Q2nRh4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +helm.sh/helm/v3 v3.18.4 h1:pNhnHM3nAmDrxz6/UC+hfjDY4yeDATQCka2/87hkZXQ= +helm.sh/helm/v3 v3.18.4/go.mod h1:WVnwKARAw01iEdjpEkP7Ii1tT1pTPYfM1HsakFKM3LI= +k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY= +k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs= +k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8= +k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8= +k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY= +k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.2 h1:KGTRbxn2wJagJowo29kKBp4TchpO1DRO3g+dB/KOJN4= +k8s.io/apiserver v0.33.2/go.mod h1:9qday04wEAMLPWWo9AwqCZSiIn3OYSZacDyu/AcoM/M= +k8s.io/cli-runtime v0.33.2 h1:koNYQKSDdq5AExa/RDudXMhhtFasEg48KLS2KSAU74Y= +k8s.io/cli-runtime v0.33.2/go.mod h1:gnhsAWpovqf1Zj5YRRBBU7PFsRc6NkEkwYNQE+mXL88= +k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E= +k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo= +k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0= +k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 h1:gAXU86Fmbr/ktY17lkHwSjw5aoThQvhnstGGIYKlKYc= +k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911/go.mod h1:GLOk5B+hDbRROvt0X2+hqX64v/zO3vXN7J78OUmBSKw= +k8s.io/kubectl v0.33.2 h1:7XKZ6DYCklu5MZQzJe+CkCjoGZwD1wWl7t/FxzhMz7Y= +k8s.io/kubectl v0.33.2/go.mod h1:8rC67FB8tVTYraovAGNi/idWIK90z2CHFNMmGJZJ3KI= +k8s.io/kubernetes v1.33.2 h1:Vk3hsCaazyMQ6CXhu029AEPlBoYsEnD8oEIC0bP2pWQ= +k8s.io/kubernetes v1.33.2/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/pkg v0.0.0-20250708013613-d3550d4350f9 h1:yCiThjArNgVjRbyBGvRvYr/B6G3djvr5pXrh8MXfnsk= +knative.dev/pkg v0.0.0-20250708013613-d3550d4350f9/go.mod h1:I2vWm0knRQH2WBfpIAkRzOPk0XoXxfu8YJT63ddYWd4= +mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI= +mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg= +oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= +oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= +sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kustomize/api v0.20.0 h1:xPLqcobHI0bThyRUteO+nCV8G4d1Rlo5HafO57VRcas= +sigs.k8s.io/kustomize/api v0.20.0/go.mod h1:F6CfaV27oevRCMJgehLqyX81dlUnRX/Fc13Uo7+OSo4= +sigs.k8s.io/kustomize/kyaml v0.20.0 h1:tT8KMKi4R3hCJ1+9HDdek2VoXpkerP92ZfF6fDgGw14= +sigs.k8s.io/kustomize/kyaml v0.20.0/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= +sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= From fb405c64abea3c9500feb8e4a34ea26db1a792a0 Mon Sep 17 00:00:00 2001 From: Slava Shklyaev Date: Thu, 17 Jul 2025 20:47:50 +0100 Subject: [PATCH 62/97] Fix #6939 --- plugins/git/git-from-archive | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/git/git-from-archive b/plugins/git/git-from-archive index fe1f71e0b..f44bf291b 100755 --- a/plugins/git/git-from-archive +++ b/plugins/git/git-from-archive @@ -42,12 +42,12 @@ trigger-git-git-from-archive() { dokku_log_verbose "Stripping top-level archive folder components" local subpath="$(find "$TMP_WORK_DIR_3" -mindepth 1 -maxdepth 1 -type d)" pushd "$subpath" >/dev/null - find . -maxdepth 1 -exec mv {} "$TMP_WORK_DIR" \; + find . -mindepth 1 -maxdepth 1 -exec mv {} "$TMP_WORK_DIR" \; popd &>/dev/null || pushd "/tmp" >/dev/null else dokku_log_verbose "Moving unarchived files and folders into place" pushd "$TMP_WORK_DIR_3" >/dev/null - find . -maxdepth 1 -exec mv {} "$TMP_WORK_DIR" \; + find . -mindepth 1 -maxdepth 1 -exec mv {} "$TMP_WORK_DIR" \; popd &>/dev/null || pushd "/tmp" >/dev/null fi From b151192394260eb91bb862ae98fdbf2a7c6f0058 Mon Sep 17 00:00:00 2001 From: Petr Hlavicka Date: Sat, 19 Jul 2025 14:52:54 +0200 Subject: [PATCH 63/97] enable key --- plugins/caddy-vhosts/subcommands/set | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/caddy-vhosts/subcommands/set b/plugins/caddy-vhosts/subcommands/set index 99954cf3e..39dc3daa2 100755 --- a/plugins/caddy-vhosts/subcommands/set +++ b/plugins/caddy-vhosts/subcommands/set @@ -9,13 +9,13 @@ cmd-caddy-set() { declare cmd="caddy:set" [[ "$1" == "$cmd" ]] && shift 1 declare APP="$1" KEY="$2" VALUE="$3" - local VALID_KEYS=("image" "letsencrypt-email" "letsencrypt-server" "log-level" "polling-interval" "tls-internal") + local VALID_KEYS=("image" "letsencrypt-email" "letsencrypt-server" "log-level" "polling-interval" "label-key" "tls-internal") local GLOBAL_KEYS=("image" "letsencrypt-email" "letsencrypt-server" "log-level" "polling-interval") [[ -z "$KEY" ]] && dokku_log_fail "No key specified" if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then - dokku_log_fail "Invalid key specified, valid keys include: image letsencrypt-email letsencrypt-server log-level polling-interval tls-internal" + dokku_log_fail "Invalid key specified, valid keys include: image letsencrypt-email letsencrypt-server log-level polling-interval label-key tls-internal" fi if ! fn-in-array "$KEY" "${GLOBAL_KEYS[@]}"; then From 22c3d3487731f7fdaebd719e1c1f927bb8c56ee6 Mon Sep 17 00:00:00 2001 From: Petr Hlavicka Date: Sat, 19 Jul 2025 14:54:01 +0200 Subject: [PATCH 64/97] add get function --- plugins/caddy-vhosts/internal-functions | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/caddy-vhosts/internal-functions b/plugins/caddy-vhosts/internal-functions index 86727e364..88758f732 100755 --- a/plugins/caddy-vhosts/internal-functions +++ b/plugins/caddy-vhosts/internal-functions @@ -71,3 +71,8 @@ fn-caddy-tls-internal() { declare APP="$1" fn-plugin-property-get-default "caddy" "$APP" "tls-internal" "false" } + +fn-caddy-label-key() { + declare APP="$1" + fn-plugin-property-get-default "caddy" "$APP" "label-key" "caddy" +} From 3c6796bd7b24c1d3da9b9c5d2f9dfc014661e07c Mon Sep 17 00:00:00 2001 From: Petr Hlavicka Date: Sat, 19 Jul 2025 15:02:57 +0200 Subject: [PATCH 65/97] use it --- plugins/caddy-vhosts/docker-args-process-deploy | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/caddy-vhosts/docker-args-process-deploy b/plugins/caddy-vhosts/docker-args-process-deploy index 65a9c9796..9dd1f61f1 100755 --- a/plugins/caddy-vhosts/docker-args-process-deploy +++ b/plugins/caddy-vhosts/docker-args-process-deploy @@ -8,7 +8,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { declare desc="nginx-vhosts core-post-deploy plugin trigger" declare trigger="docker-args-process-deploy" declare APP="$1" IMAGE_SOURCE_TYPE="$2" IMAGE_TAG="$3" PROC_TYPE="$4" CONTAINER_INDEX="$5" - local app_domains caddy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_scheme proxy_schemes scheme tls_internal + local app_domains caddy_domains is_app_listening letsencrypt_email output proxy_container_port proxy_host_port port_map proxy_scheme proxy_schemes scheme tls_internal label_key local proxy_container_http_port proxy_container_http_port_candidate proxy_host_http_port_candidate local proxy_container_https_port proxy_container_https_port_candidate proxy_host_https_port_candidate local STDIN=$(cat) @@ -88,13 +88,14 @@ trigger-caddy-vhosts-docker-args-process-deploy() { if [[ -n "$is_app_listening" ]] && [[ -n "$caddy_domains" ]]; then has_443_mapping=false tls_internal="$(fn-caddy-tls-internal)" + label_key="$(fn-caddy-label-key "$APP")" if [[ -n "$proxy_container_https_port" ]] || [[ -n "$proxy_container_https_port_candidate" ]]; then has_443_mapping=true fi ssl_warning_mapping="https:443" if [[ "$tls_internal" == "true" ]]; then - output="--label caddy.tls=internal" + output="--label ${label_key}.tls=internal" if [[ "$has_443_mapping" == "false" ]]; then ssl_warning_mapping="http:80" proxy_host_https_port_candidate="$proxy_host_http_port_candidate" @@ -105,7 +106,7 @@ trigger-caddy-vhosts-docker-args-process-deploy() { scheme="http" if [[ -n "$letsencrypt_email" ]] && [[ "$has_443_mapping" == "true" ]]; then - output="--label 'caddy=${caddy_domains}'" + output="--label '${label_key}=${caddy_domains}'" scheme="https" if [[ -z "$proxy_container_https_port" ]]; then warning_scheme="$(awk -F ':' '{ print $1 }' <<<"$ssl_warning_mapping")" @@ -114,17 +115,17 @@ trigger-caddy-vhosts-docker-args-process-deploy() { proxy_container_https_port="$proxy_container_https_port_candidate" fi - output="$output --label \"caddy.reverse_proxy={{ upstreams $proxy_container_https_port }}\"" + output="$output --label \"${label_key}.reverse_proxy={{ upstreams $proxy_container_https_port }}\"" elif [[ -n "$proxy_container_http_port" ]] || [[ -n "$proxy_container_http_port_candidate" ]]; then caddy_domains="${caddy_domains//, /:80, }" - output="--label 'caddy=${caddy_domains}:80'" + output="--label '${label_key}=${caddy_domains}:80'" if [[ -z "$proxy_container_http_port" ]]; then dokku_log_warn "Warning: http:80 port mapping not found" dokku_log_warn "Utilizing first http port mapping, http:$proxy_host_http_port_candidate:$proxy_container_http_port_candidate" proxy_container_http_port="$proxy_container_http_port_candidate" fi - output="$output --label \"caddy.reverse_proxy={{ upstreams $proxy_container_http_port }}\"" + output="$output --label \"${label_key}.reverse_proxy={{ upstreams $proxy_container_http_port }}\"" fi fi From bf3b2d396bcda57181c19991e284347d4e0a0b5a Mon Sep 17 00:00:00 2001 From: Petr Hlavicka Date: Sat, 19 Jul 2025 15:37:17 +0200 Subject: [PATCH 66/97] update docs --- docs/networking/proxies/caddy.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/networking/proxies/caddy.md b/docs/networking/proxies/caddy.md index fe2f6b9c7..b56ab994d 100644 --- a/docs/networking/proxies/caddy.md +++ b/docs/networking/proxies/caddy.md @@ -117,6 +117,16 @@ dokku caddy:set --global log-level DEBUG After modifying, the Caddy container will need to be restarted. +### Changing the label key for the app + +The default label key for the app is `caddy`. This can be changed by setting the `label-key` property: + +```shell +dokku caddy:set node-js-app label-key caddy_0 +``` + +This will update the app's label key to `caddy_0`. The label key is used to identify the app in the Caddy configuration. If you change the label key, you will need to restart or rebuild the app to apply the updates. + ### SSL Configuration The caddy plugin only supports automatic ssl certificates from it's letsencrypt integration. Managed certificates provided by the `certs` plugin are ignored. From acf2c854d2485a221d27389dd550067aacabb9ec Mon Sep 17 00:00:00 2001 From: Petr Hlavicka Date: Sat, 19 Jul 2025 15:53:48 +0200 Subject: [PATCH 67/97] test --- tests/unit/caddy.bats | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/unit/caddy.bats b/tests/unit/caddy.bats index d48d41196..0155cda71 100644 --- a/tests/unit/caddy.bats +++ b/tests/unit/caddy.bats @@ -50,6 +50,34 @@ teardown() { assert_output_contains "python/http.server" } +@test "(caddy) custom label key" { + run /bin/bash -c "dokku builder-herokuish:set $TEST_APP allowed true" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku proxy:set $TEST_APP caddy" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku caddy:set $TEST_APP label-key caddy_0" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "docker inspect $TEST_APP.web.1 --format '{{ index .Config.Labels \"caddy_0\" }}'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "$TEST_APP.${DOKKU_DOMAIN}:80" +} + @test "(caddy) multiple domains" { run /bin/bash -c "dokku proxy:set $TEST_APP caddy" echo "output: $output" From 577f46153fb7b04b791e1f3322ca8e4ff2df50aa Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 17:55:45 -0400 Subject: [PATCH 68/97] refactor: use helper go functions to handle extracting files from a repository This standardizes the extraction code and makes it easier to reuse in future implementations of file extraction. Additionally, this adds a way to extract folders from codebases. --- plugins/app-json/triggers.go | 114 ++++------- plugins/common/common.go | 293 ++++++++++++++++++++++++++++ plugins/common/docker.go | 92 +++++++++ plugins/common/functions | 51 +---- plugins/common/io.go | 18 +- plugins/common/src/common/common.go | 6 + plugins/ps/triggers.go | 107 ++++------ 7 files changed, 483 insertions(+), 198 deletions(-) diff --git a/plugins/app-json/triggers.go b/plugins/app-json/triggers.go index 08bd24404..06b5126e0 100644 --- a/plugins/app-json/triggers.go +++ b/plugins/app-json/triggers.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "os" - "path" - "path/filepath" "strings" "github.com/dokku/dokku/plugins/common" @@ -59,95 +57,39 @@ func TriggerAppJSONGetContent(appName string) error { return nil } -// TriggerCorePostDeploy sets a property to -// allow the app to be restored on boot +// TriggerCorePostDeploy moves the extracted app.json to the app data directory +// allowing the app to be restored on boot func TriggerCorePostDeploy(appName string) error { - existingAppJSON := getAppJSONPath(appName) - processSpecificAppJSON := fmt.Sprintf("%s.%s", existingAppJSON, os.Getenv("DOKKU_PID")) - if common.FileExists(processSpecificAppJSON) { - if err := os.Rename(processSpecificAppJSON, existingAppJSON); err != nil { - return err - } - } else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificAppJSON)) { - if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificAppJSON)); err != nil { - return err - } - - if common.FileExists(existingAppJSON) { - if err := os.Remove(existingAppJSON); err != nil { - return err - } - } - } - - return nil + return common.CorePostDeploy(common.CorePostDeployInput{ + AppName: appName, + Destination: common.GetAppDataDirectory("app-json", appName), + PluginName: "app-json", + ExtractedPaths: []common.CorePostDeployPath{ + {Path: "app.json", IsDirectory: false}, + }, + }) } // TriggerCorePostExtract ensures that the main app.json is the one specified by app-json-path func TriggerCorePostExtract(appName string, sourceWorkDir string) error { + destination := common.GetAppDataDirectory("app-json", appName) appJSONPath := strings.Trim(reportComputedAppjsonpath(appName), "/") if appJSONPath == "" { appJSONPath = "app.json" } - existingAppJSON := getAppJSONPath(appName) - files, err := filepath.Glob(fmt.Sprintf("%s.*", existingAppJSON)) - if err != nil { - return err - } - for _, f := range files { - if err := os.Remove(f); err != nil { - return err - } - } - - processSpecificAppJSON := fmt.Sprintf("%s.%s", existingAppJSON, os.Getenv("DOKKU_PID")) - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "git-get-property", - Args: []string{appName, "source-image"}, - }) - appSourceImage := results.StdoutContents() - - results, _ = common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "builder-get-property", - Args: []string{appName, "build-dir"}, - }) - buildDir := results.StdoutContents() - - repoDefaultAppJSONPath := path.Join(sourceWorkDir, "app.json") - if appSourceImage == "" { - repoAppJSONPath := path.Join(sourceWorkDir, buildDir, appJSONPath) - if !common.FileExists(repoAppJSONPath) { - if appJSONPath != "app.json" && common.FileExists(repoDefaultAppJSONPath) { - if err := os.Remove(repoDefaultAppJSONPath); err != nil { - return err - } - } - return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificAppJSON)) + validator := func(appName string, path string) error { + if !common.FileExists(path) { + return nil } - if err := common.Copy(repoAppJSONPath, processSpecificAppJSON); err != nil { - return fmt.Errorf("Unable to extract app.json: %v", err.Error()) - } - - if appJSONPath != "app.json" { - if err := common.Copy(repoAppJSONPath, repoDefaultAppJSONPath); err != nil { - return fmt.Errorf("Unable to move app.json into place: %v", err.Error()) - } - } - } else { - if err := common.CopyFromImage(appName, appSourceImage, path.Join(buildDir, appJSONPath), processSpecificAppJSON); err != nil { - return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificAppJSON)) - } - } - - if common.FileExists(processSpecificAppJSON) { result, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "app-json-is-valid", - Args: []string{appName, processSpecificAppJSON}, + Args: []string{appName, path}, StreamStdout: true, StreamStderr: true, }) + if err != nil { if result.StderrContents() != "" { return errors.New(result.StderrContents()) @@ -155,10 +97,30 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { return err } + return nil } - // TODO: add validation to app.json file by ensuring it can be deserialized - return nil + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "builder-get-property", + Args: []string{appName, "build-dir"}, + }) + buildDir := results.StdoutContents() + return common.CorePostExtract(common.CorePostExtractInput{ + AppName: appName, + BuildDir: buildDir, + Destination: destination, + PluginName: "app-json", + SourceWorkDir: sourceWorkDir, + ToExtract: []common.CorePostExtractToExtract{ + { + Path: appJSONPath, + IsDirectory: false, + Name: "app.json", + Destination: "app.json", + Validator: validator, + }, + }, + }) } // TriggerInstall initializes app-json directory structures diff --git a/plugins/common/common.go b/plugins/common/common.go index 5a101831a..a8ae3e662 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -14,6 +14,7 @@ import ( "strings" "unicode" + "github.com/otiai10/copy" "github.com/ryanuber/columnize" "golang.org/x/sync/errgroup" ) @@ -74,6 +75,298 @@ func CommandUsage(helpHeader string, helpContent string) { fmt.Println(columnize.Format(content, config)) } +// CorePostDeployPath is a file or directory that was extracted +type CorePostDeployPath struct { + // IsDirectory is whether the source is a directory + IsDirectory bool + + // Path is the name of the file or directory + Path string +} + +// CorePostDeployInput is the input for the CorePostDeploy function +type CorePostDeployInput struct { + // AppName is the name of the app + AppName string + + // Destination is the destination directory + Destination string + + // PluginName is the name of the plugin that is deploying the file or directory + PluginName string + + // ExtractedPaths is the list of paths that were extracted + ExtractedPaths []CorePostDeployPath +} + +// CorePostDeploy moves extracted paths to the destination directory +// and removes any existing files or directories that were not extracted +// +// CorePostDeploy(CorePostDeployInput{ +// AppName: "my-app", +// Destination: "/var/lib/dokku/data/my-app", +// ExtractedPaths: []CorePostDeployPath{ +// {Name: "app.json", IsDirectory: false}, +// {Name: "kustomization", IsDirectory: true}, +// }, +// }) +func CorePostDeploy(input CorePostDeployInput) error { + if input.PluginName == "" { + return fmt.Errorf("Missing required PluginName in CorePostDeploy") + } + + if input.AppName == "" { + return fmt.Errorf("Missing required AppName in CorePostDeploy for plugin %v", input.PluginName) + } + + if input.Destination == "" { + return fmt.Errorf("Missing required Destination in CorePostDeploy for plugin %v", input.PluginName) + } + + for i, extractedPath := range input.ExtractedPaths { + if extractedPath.Path == "" { + return fmt.Errorf("Missing required Name in CorePostDeploy for index %v for plugin %v", i, input.PluginName) + } + + existingPath := filepath.Join(input.Destination, extractedPath.Path) + processSpecificPath := fmt.Sprintf("%s.%s", existingPath, os.Getenv("DOKKU_PID")) + + if extractedPath.IsDirectory { + if DirectoryExists(processSpecificPath) { + if err := os.RemoveAll(existingPath); err != nil { + return err + } + + if err := os.Rename(processSpecificPath, existingPath); err != nil { + return err + } + } else if DirectoryExists(fmt.Sprintf("%s.missing", processSpecificPath)) { + if err := os.RemoveAll(fmt.Sprintf("%s.missing", processSpecificPath)); err != nil { + return err + } + + if DirectoryExists(existingPath) { + if err := os.RemoveAll(existingPath); err != nil { + return err + } + } + } + } else { + if FileExists(processSpecificPath) { + if err := os.Rename(processSpecificPath, existingPath); err != nil { + return err + } + } else if FileExists(fmt.Sprintf("%s.missing", processSpecificPath)) { + if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificPath)); err != nil { + return err + } + + if FileExists(existingPath) { + if err := os.Remove(existingPath); err != nil { + return err + } + } + } + } + } + + return nil +} + +// CorePostExtractValidator is a function that validates the file or directory +type CorePostExtractValidator func(appName string, path string) error + +// CorePostExtractToExtract is a file or directory to extract +type CorePostExtractToExtract struct { + // Destination is an optional alias destination path + // If not provided, the Path will be used as the destination + Destination string + + // IsDirectory is whether the source is a directory + IsDirectory bool + + // Name is the common name of the file or directory to extract + Name string + + // Path is the path to the file or directory to extract + Path string + + // Validator is a function that validates the file or directory + Validator CorePostExtractValidator +} + +// CorePostExtractInput is the input for the CorePostExtract function +type CorePostExtractInput struct { + // AppName is the name of the app + AppName string + + // BuildDir is the optional build directory to extract from + BuildDir string + + // DestinationDir is the destination directory + Destination string + + // PluginName is the name of the plugin that is extracting the file or directory + PluginName string + + // SourceWorkDir is the source work directory + SourceWorkDir string + + // ToExtract is a list of files or directories to extract + ToExtract []CorePostExtractToExtract +} + +// CorePostExtract extracts files or directories from a source work directory to a destination directory +// +// CorePostExtract(CorePostExtractInput{ +// AppName: "my-app", +// SourceWorkDir: "/tmp/my-app-source", +// Destination: "/var/lib/dokku/data/my-app", +// ToExtract: []CorePostExtractToExtract{ +// {Path: "app2.json", IsDirectory: false, Name: "app.json"}, +// {Path: "config/kustomize", IsDirectory: true, Destination: "kustomization"}, +// }, +// }) +func CorePostExtract(input CorePostExtractInput) error { + if input.PluginName == "" { + return fmt.Errorf("Missing required PluginName in CorePostExtract") + } + + if input.AppName == "" { + return fmt.Errorf("Missing required AppName in CorePostExtract for plugin %v", input.PluginName) + } + + if input.Destination == "" { + return fmt.Errorf("Missing required Destination in CorePostExtract for plugin %v", input.PluginName) + } + + if input.SourceWorkDir == "" { + return fmt.Errorf("Missing required SourceWorkDir in CorePostExtract for plugin %v", input.PluginName) + } + + results, _ := CallPlugnTrigger(PlugnTriggerInput{ + Trigger: "git-get-property", + Args: []string{input.AppName, "source-image"}, + }) + sourceImage := results.StdoutContents() + + for i, toExtract := range input.ToExtract { + if toExtract.Name == "" { + return fmt.Errorf("Name is required for index %v in CorePostExtract for plugin %v", i, input.PluginName) + } + + if toExtract.Path == "" { + return fmt.Errorf("Path is required for index %v in CorePostExtract for plugin %v", i, input.PluginName) + } + + if toExtract.Destination == "" { + toExtract.Destination = toExtract.Path + } + + sourcePath := filepath.Join(input.SourceWorkDir, toExtract.Path) + repoDefaultSourcePath := filepath.Join(input.SourceWorkDir, toExtract.Name) + imageSourcePath := toExtract.Path + if input.BuildDir != "" { + sourcePath = filepath.Join(input.SourceWorkDir, input.BuildDir, toExtract.Path) + repoDefaultSourcePath = filepath.Join(input.SourceWorkDir, input.BuildDir, toExtract.Name) + imageSourcePath = filepath.Join(input.BuildDir, toExtract.Path) + } + + destination := filepath.Join(input.Destination, toExtract.Destination) + processSpecificDestination := fmt.Sprintf("%s.%s", destination, os.Getenv("DOKKU_PID")) + missingDestination := fmt.Sprintf("%s.missing", processSpecificDestination) + files, err := filepath.Glob(fmt.Sprintf("%s.*", destination)) + if err != nil { + return err + } + for _, f := range files { + if err := os.Remove(f); err != nil { + return err + } + } + + // ignore if the path is empty + if toExtract.Path == "" { + if err := TouchFile(missingDestination); err != nil { + return err + } + continue + } + + if sourceImage == "" { + // ignore if the file does not exist + if toExtract.IsDirectory { + if !DirectoryExists(sourcePath) { + if sourcePath != repoDefaultSourcePath && DirectoryExists(repoDefaultSourcePath) { + if err := os.RemoveAll(repoDefaultSourcePath); err != nil { + return fmt.Errorf("Unable to remove existing %v: %s", toExtract.Name, err.Error()) + } + } + + if err := TouchFile(missingDestination); err != nil { + return err + } + continue + } + + if err := Copy(sourcePath, processSpecificDestination); err != nil { + return fmt.Errorf("Unable to extract %v from %v: %s", toExtract.Name, toExtract.Path, err.Error()) + } + + if sourcePath != repoDefaultSourcePath { + if err := Copy(sourcePath, repoDefaultSourcePath); err != nil { + return fmt.Errorf("Unable to move %v into place: %s", toExtract.Name, err.Error()) + } + } + } else { + if !FileExists(sourcePath) { + // delete the existing file if the user tried to override it with a non-existent file + if sourcePath != repoDefaultSourcePath && FileExists(repoDefaultSourcePath) { + if err := os.Remove(repoDefaultSourcePath); err != nil { + return err + } + } + if err := TouchFile(missingDestination); err != nil { + return err + } + continue + } + + if err := Copy(sourcePath, processSpecificDestination); err != nil { + return fmt.Errorf("Unable to extract %v from %v: %v", toExtract.Name, toExtract.Path, err.Error()) + } + + if sourcePath != repoDefaultSourcePath { + // ensure the file in the repo is the same as the one the user specified + if err := copy.Copy(sourcePath, repoDefaultSourcePath); err != nil { + return fmt.Errorf("Unable to move %v into place: %v", toExtract.Name, err.Error()) + } + } + } + } else { + if toExtract.IsDirectory { + + if err := CopyDirFromImage(input.AppName, sourceImage, imageSourcePath, processSpecificDestination); err != nil { + return TouchFile(missingDestination) + } + } else { + if err := CopyFromImage(input.AppName, sourceImage, imageSourcePath, processSpecificDestination); err != nil { + return TouchFile(missingDestination) + } + } + } + + // validate the file + if toExtract.Validator != nil { + if err := toExtract.Validator(input.AppName, processSpecificDestination); err != nil { + return err + } + } + } + + return nil +} + // EnvWrap wraps a func with a setenv call and resets the value at the end func EnvWrap(fn func() error, environ map[string]string) error { oldEnviron := map[string]string{} diff --git a/plugins/common/docker.go b/plugins/common/docker.go index bf3cef0cd..11a0c1a39 100644 --- a/plugins/common/docker.go +++ b/plugins/common/docker.go @@ -151,6 +151,98 @@ func ContainerWaitTilReady(containerID string, timeout time.Duration) error { return nil } +// CopyDirFromImage copies a directory from named image to destination +func CopyDirFromImage(appName string, image string, source string, destination string) error { + if !VerifyImage(image) { + return fmt.Errorf("Invalid docker image for copying content") + } + + if !IsAbsPath(source) { + workDir := GetWorkingDir(appName, image) + if workDir != "" { + source = fmt.Sprintf("%s/%s", workDir, source) + } + } + + tmpDir, err := os.MkdirTemp("", fmt.Sprintf("dokku-%s-%s", MustGetEnv("DOKKU_PID"), "CopyFromImage")) + if err != nil { + return fmt.Errorf("Error creating temporary directory: %v", err) + } + + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + LogWarn(fmt.Sprintf("Error removing temporary directory %s: %v\n", tmpDir, err)) + } + }() + + globalRunArgs := MustGetEnv("DOKKU_GLOBAL_RUN_ARGS") + createLabelArgs := []string{"--label", fmt.Sprintf("com.dokku.app-name=%s", appName), globalRunArgs} + containerID, err := DockerContainerCreate(image, createLabelArgs) + if err != nil { + return fmt.Errorf("Unable to create temporary container: %v", err) + } + defer ContainerRemove(containerID) + + // docker cp exits with status 1 when run as non-root user when it tries to chown the file + // after successfully copying the file. Thus, we suppress stderr. + // ref: https://github.com/dotcloud/docker/issues/3986 + result, err := CallExecCommand(ExecCommandInput{ + Command: DockerBin(), + Args: []string{"container", "cp", "--quiet", fmt.Sprintf("%s:%s", containerID, source), tmpDir}, + }) + if err != nil { + return fmt.Errorf("Unable to copy file %s from image: %w", source, err) + } + if result.ExitCode != 0 { + return fmt.Errorf("Unable to copy file %s from image: %v", source, result.StderrContents()) + } + + if !DirectoryExists(tmpDir) { + return fmt.Errorf("Unable to copy file %s from image: %v", source, result.StderrContents()) + } + + files, err := os.ReadDir(tmpDir) + if err != nil { + return fmt.Errorf("Unable to read temporary directory: %v", err) + } + + for _, file := range files { + if file.IsDir() { + continue + } + + sourceFile := fmt.Sprintf("%s/%s", tmpDir, file.Name()) + destinationFile := fmt.Sprintf("%s/%s", destination, file.Name()) + // workaround when owner is root. seems to only happen when running inside docker + CallExecCommand(ExecCommandInput{ + Command: "dos2unix", + Args: []string{"-l", "-n", sourceFile, destinationFile}, + }) // nolint: errcheck + + // add trailing newline for certain places where file parsing depends on it + result, err = CallExecCommand(ExecCommandInput{ + Command: "tail", + Args: []string{"-c1", destination}, + }) + if err != nil || result.ExitCode != 0 { + return fmt.Errorf("Unable to append trailing newline to copied file: %v", result.Stderr) + } + + if result.Stdout != "" { + f, err := os.OpenFile(destination, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + if _, err := f.WriteString("\n"); err != nil { + return fmt.Errorf("Unable to append trailing newline to copied file: %v", err) + } + } + } + + return nil +} + // CopyFromImage copies a file from named image to destination func CopyFromImage(appName string, image string, source string, destination string) error { if !VerifyImage(image) { diff --git a/plugins/common/functions b/plugins/common/functions index 73beec9c1..f98068aac 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -488,7 +488,6 @@ parse_args() { copy_from_image() { declare desc="copy file from named image to destination" declare IMAGE="$1" SRC_FILE="$2" DST_FILE="$3" - local WORK_DIR="" if ! "$PLUGIN_CORE_AVAILABLE_PATH/common/common" copy-from-image "$APP" "$IMAGE" "$SRC_FILE" "$DST_FILE"; then return 1 @@ -498,56 +497,8 @@ copy_from_image() { copy_dir_from_image() { declare desc="copy a directory from named image to destination" declare IMAGE="$1" SRC_DIR="$2" DST_DIR="$3" - local WORK_DIR="" - local DOCKER_CREATE_LABEL_ARGS="--label=com.dokku.app-name=$APP" - - if verify_image "$IMAGE"; then - if ! is_abs_path "$SRC_DIR"; then - if is_image_cnb_based "$IMAGE"; then - WORKDIR="/workspace" - elif is_image_herokuish_based "$IMAGE" "$APP"; then - WORKDIR="/app" - else - WORKDIR="$("$DOCKER_BIN" image inspect --format '{{.Config.WorkingDir}}' "$IMAGE")" - fi - - if [[ -n "$WORKDIR" ]]; then - SRC_DIR="${WORKDIR}/${SRC_DIR}" - fi - fi - - TMP_DIR_COMMAND_OUTPUT=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX") - trap "rm -rf '$TMP_DIR_COMMAND_OUTPUT' &>/dev/null || true" RETURN - - local CID=$("$DOCKER_BIN" container create "${DOCKER_CREATE_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS "$IMAGE") - "$DOCKER_BIN" container cp "$CID:$SRC_DIR" "$TMP_DIR_COMMAND_OUTPUT" 2>/dev/null || true - "$DOCKER_BIN" container rm --force "$CID" &>/dev/null - plugn trigger scheduler-register-retired "$APP" "$CID" - - # docker cp exits with status 1 when run as non-root user when it tries to chown the file - # after successfully copying the file. Thus, we suppress stderr. - # ref: https://github.com/dotcloud/docker/issues/3986 - if [[ ! -d "$TMP_DIR_COMMAND_OUTPUT" ]]; then - return 1 - fi - - pushd "$TMP_DIR_COMMAND_OUTPUT" >/dev/null - for filename in *; do - if [[ ! -f "$TMP_DIR_COMMAND_OUTPUT/$filename" ]]; then - continue - fi - - # workaround for CHECKS file when owner is root. seems to only happen when running inside docker - dos2unix -l <"$TMP_DIR_COMMAND_OUTPUT/$filename" >"$DST_DIR/$filename" - - # add trailing newline for certain places where file parsing depends on it - if [[ "$(tail -c1 "$DST_DIR/$filename")" != "" ]]; then - echo "" >>"$DST_DIR/$filename" - fi - done - popd &>/dev/null || pushd "/tmp" >/dev/null - else + if ! "$PLUGIN_CORE_AVAILABLE_PATH/common/common" copy-dir-from-image "$APP" "$IMAGE" "$SRC_DIR" "$DST_DIR"; then return 1 fi } diff --git a/plugins/common/io.go b/plugins/common/io.go index 5ad0cf33b..1bafce8ca 100644 --- a/plugins/common/io.go +++ b/plugins/common/io.go @@ -195,23 +195,33 @@ func SetPermissions(input SetPermissionInput) error { return os.Chown(input.Filename, uid, gid) } +// TouchDir creates an empty directory at the specified path +func TouchDir(filename string) error { + mode := os.FileMode(0700) + return os.MkdirAll(filename, mode) +} + // TouchFile creates an empty file at the specified path func TouchFile(filename string) error { mode := os.FileMode(0600) file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) if err != nil { - return err + return fmt.Errorf("Error opening file %v for creation: %v", filename, err) } defer file.Close() if err := file.Chmod(mode); err != nil { - return err + return fmt.Errorf("Error setting chown for new file %v: %v", filename, err) } - return SetPermissions(SetPermissionInput{ + if err := SetPermissions(SetPermissionInput{ Filename: filename, Mode: mode, - }) + }); err != nil { + return fmt.Errorf("Error setting permissions for new file %v: %v", filename, err) + } + + return nil } // WriteSliceToFile writes a slice of strings to a file diff --git a/plugins/common/src/common/common.go b/plugins/common/src/common/common.go index 7d59e39d4..5c95746aa 100644 --- a/plugins/common/src/common/common.go +++ b/plugins/common/src/common/common.go @@ -34,6 +34,12 @@ func main() { ProjectName: projectName, ComposeFile: composeFile, }) + case "copy-dir-from-image": + appName := flag.Arg(1) + image := flag.Arg(2) + source := flag.Arg(3) + destination := flag.Arg(4) + err = common.CopyDirFromImage(appName, image, source, destination) case "copy-from-image": appName := flag.Arg(1) image := flag.Arg(2) diff --git a/plugins/ps/triggers.go b/plugins/ps/triggers.go index 4d788827c..ad69a9f61 100644 --- a/plugins/ps/triggers.go +++ b/plugins/ps/triggers.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "path" "path/filepath" "strconv" "strings" @@ -22,22 +21,16 @@ func TriggerAppRestart(appName string) error { // TriggerCorePostDeploy sets a property to // allow the app to be restored on boot func TriggerCorePostDeploy(appName string) error { - existingProcfile := getProcfilePath(appName) - processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID")) - if common.FileExists(processSpecificProcfile) { - if err := os.Rename(processSpecificProcfile, existingProcfile); err != nil { - return err - } - } else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificProcfile)) { - if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificProcfile)); err != nil { - return err - } - - if common.FileExists(existingProcfile) { - if err := os.Remove(existingProcfile); err != nil { - return err - } - } + err := common.CorePostDeploy(common.CorePostDeployInput{ + AppName: appName, + Destination: common.GetAppDataDirectory("ps", appName), + PluginName: "ps", + ExtractedPaths: []common.CorePostDeployPath{ + {Path: "Procfile", IsDirectory: false}, + }, + }) + if err != nil { + return err } if err := common.PropertyDelete("ps", appName, "scale.old"); err != nil { @@ -55,67 +48,45 @@ func TriggerCorePostDeploy(appName string) error { // TriggerCorePostExtract ensures that the main Procfile is the one specified by procfile-path func TriggerCorePostExtract(appName string, sourceWorkDir string) error { + destination := common.GetAppDataDirectory("ps", appName) procfilePath := strings.Trim(reportComputedProcfilePath(appName), "/") if procfilePath == "" { procfilePath = "Procfile" } - existingProcfile := getProcfilePath(appName) - files, err := filepath.Glob(fmt.Sprintf("%s.*", existingProcfile)) - if err != nil { - return err - } - for _, f := range files { - if err := os.Remove(f); err != nil { + validator := func(appName string, path string) error { + if !common.FileExists(path) { + return nil + } + + result, err := common.CallExecCommand(common.ExecCommandInput{ + Command: "procfile-util", + Args: []string{"check", "-P", path}, + }) + if err != nil { return err } + if result.ExitCode != 0 { + return fmt.Errorf("Invalid Procfile: %s", result.StderrContents()) + } + return nil } - processSpecificProcfile := fmt.Sprintf("%s.%s", existingProcfile, os.Getenv("DOKKU_PID")) - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "git-get-property", - Args: []string{appName, "source-image"}, + return common.CorePostExtract(common.CorePostExtractInput{ + AppName: appName, + Destination: destination, + PluginName: "ps", + SourceWorkDir: sourceWorkDir, + ToExtract: []common.CorePostExtractToExtract{ + { + Path: procfilePath, + IsDirectory: false, + Name: "Procfile", + Destination: "Procfile", + Validator: validator, + }, + }, }) - appSourceImage := results.StdoutContents() - - repoDefaultProcfilePath := path.Join(sourceWorkDir, "Procfile") - if appSourceImage == "" { - repoProcfilePath := path.Join(sourceWorkDir, procfilePath) - if !common.FileExists(repoProcfilePath) { - if procfilePath != "Procfile" && common.FileExists(repoDefaultProcfilePath) { - if err := os.Remove(repoDefaultProcfilePath); err != nil { - return err - } - } - return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificProcfile)) - } - - if err := common.Copy(repoProcfilePath, processSpecificProcfile); err != nil { - return fmt.Errorf("Unable to extract Procfile: %v", err.Error()) - } - - if procfilePath != "Procfile" { - if err := common.Copy(repoProcfilePath, repoDefaultProcfilePath); err != nil { - return fmt.Errorf("Unable to move Procfile into place: %v", err.Error()) - } - } - } else { - if err := common.CopyFromImage(appName, appSourceImage, procfilePath, processSpecificProcfile); err != nil { - return common.TouchFile(fmt.Sprintf("%s.missing", processSpecificProcfile)) - } - } - - result, err := common.CallExecCommand(common.ExecCommandInput{ - Command: "procfile-util", - Args: []string{"check", "-P", processSpecificProcfile}, - }) - if err != nil { - return fmt.Errorf(result.StderrContents()) - } - if result.ExitCode != 0 { - return fmt.Errorf("Invalid Procfile: %s", result.StderrContents()) - } - return nil } // TriggerInstall initializes app restart policies From e034421ea9ce42d320133ae6a3bcfa8f80e13348 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 00:42:57 -0400 Subject: [PATCH 69/97] fix: ensure the InstallChart function also attaches the debug renderer --- plugins/scheduler-k3s/helm.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index 8f007cdf1..da4cd008c 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -242,6 +242,10 @@ func (h *HelmAgent) InstallChart(ctx context.Context, input ChartInput) error { client.ChartPathOptions = action.ChartPathOptions{} client.CreateNamespace = true client.DryRun = false + if os.Getenv("DOKKU_TRACE") == "1" { + client.DryRun = true + client.PostRenderer = &DebugRenderer{} + } client.Namespace = namespace client.ReleaseName = input.ReleaseName client.Timeout = input.Timeout From 5ae6c1c84531c1d0774a0801a26e8f9d813bd074 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 20:52:26 -0400 Subject: [PATCH 70/97] feat: add the ability to specify a kustomize-root-path This will default to `config/kustomize`, though may change to `.dokku/kustomize` in the future. --- plugins/scheduler-k3s/functions.go | 17 +++++++++++++++++ plugins/scheduler-k3s/report.go | 15 +++++++++++++++ plugins/scheduler-k3s/scheduler_k3s.go | 2 ++ 3 files changed, 34 insertions(+) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 942d5c4f2..a7ec92f92 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1190,6 +1190,23 @@ func getGlobalLetsencryptEmailStag() string { return common.PropertyGetDefault("scheduler-k3s", "--global", "letsencrypt-email-stag", "") } +func getKustomizeRootPath(appName string) string { + return common.PropertyGetDefault("scheduler-k3s", appName, "kustomize-root-path", "") +} + +func getGlobalKustomizeRootPath() string { + return common.PropertyGetDefault("scheduler-k3s", "--global", "kustomize-root-path", "config/kustomize") +} + +func getComputedKustomizeRootPath(appName string) string { + kustomizeRootPath := getKustomizeRootPath(appName) + if kustomizeRootPath == "" { + kustomizeRootPath = getGlobalKustomizeRootPath() + } + + return kustomizeRootPath +} + func getNamespace(appName string) string { return common.PropertyGetDefault("scheduler-k3s", appName, "namespace", "") } diff --git a/plugins/scheduler-k3s/report.go b/plugins/scheduler-k3s/report.go index d480969b6..d1bb6d2de 100644 --- a/plugins/scheduler-k3s/report.go +++ b/plugins/scheduler-k3s/report.go @@ -28,6 +28,9 @@ func ReportSingleApp(appName string, format string, infoFlag string) error { "--scheduler-k3s-global-ingress-class": reportGlobalIngressClass, "--scheduler-k3s-global-letsencrypt-email-prod": reportGlobalLetsencryptEmailProd, "--scheduler-k3s-global-letsencrypt-email-stag": reportGlobalLetsencryptEmailStag, + "--scheduler-k3s-computed-kustomize-root-path": reportComputedKustomizeRootPath, + "--scheduler-k3s-kustomize-root-path": reportKustomizeRootPath, + "--scheduler-k3s-global-kustomize-root-path": reportGlobalKustomizeRootPath, "--scheduler-k3s-computed-namespace": reportComputedNamespace, "--scheduler-k3s-namespace": reportNamespace, "--scheduler-k3s-global-namespace": reportGlobalNamespace, @@ -148,6 +151,18 @@ func reportGlobalLetsencryptEmailStag(appName string) string { return getGlobalLetsencryptEmailStag() } +func reportComputedKustomizeRootPath(appName string) string { + return getComputedKustomizeRootPath(appName) +} + +func reportKustomizeRootPath(appName string) string { + return getKustomizeRootPath(appName) +} + +func reportGlobalKustomizeRootPath(appName string) string { + return getGlobalKustomizeRootPath() +} + func reportComputedNamespace(appName string) string { return getComputedNamespace(appName) } diff --git a/plugins/scheduler-k3s/scheduler_k3s.go b/plugins/scheduler-k3s/scheduler_k3s.go index 9a37a78bd..fbf29a7bc 100644 --- a/plugins/scheduler-k3s/scheduler_k3s.go +++ b/plugins/scheduler-k3s/scheduler_k3s.go @@ -20,6 +20,7 @@ var ( DefaultProperties = map[string]string{ "deploy-timeout": "", "letsencrypt-server": "", + "kustomize-root-path": "", "image-pull-secrets": "", "namespace": "", "rollback-on-failure": "", @@ -33,6 +34,7 @@ var ( "ingress-class": true, "kube-context": true, "kubeconfig-path": true, + "kustomize-root-path": true, "letsencrypt-server": true, "letsencrypt-email-prod": true, "letsencrypt-email-stag": true, From 1b4e02eb6234eb3492a944e5d9647543f164bcfe Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 8 Jul 2025 22:30:20 -0400 Subject: [PATCH 71/97] feat: create a KustomizeRenderer The KustomizeRenderer is used to invoke Kustomize to the generated renderer.yaml. --- plugins/scheduler-k3s/go.mod | 3 +- plugins/scheduler-k3s/go.sum | 2 + plugins/scheduler-k3s/helm.go | 89 +++++++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/plugins/scheduler-k3s/go.mod b/plugins/scheduler-k3s/go.mod index 2e7f2df80..bf48e9f6f 100644 --- a/plugins/scheduler-k3s/go.mod +++ b/plugins/scheduler-k3s/go.mod @@ -11,6 +11,7 @@ require ( github.com/dokku/dokku/plugins/logs v0.0.0-20250618161309-8d0c35f1333c github.com/dokku/dokku/plugins/nginx-vhosts v0.0.0-20250618161309-8d0c35f1333c github.com/fatih/color v1.18.0 + github.com/fluxcd/pkg/kustomize v1.18.0 github.com/go-openapi/jsonpointer v0.21.1 github.com/go-resty/resty/v2 v2.16.5 github.com/gofrs/flock v0.12.1 @@ -30,6 +31,7 @@ require ( k8s.io/kubernetes v1.33.2 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 mvdan.cc/sh/v3 v3.12.0 + sigs.k8s.io/kustomize/api v0.20.0 ) require ( @@ -168,7 +170,6 @@ require ( sigs.k8s.io/controller-runtime v0.21.0 // indirect sigs.k8s.io/gateway-api v1.3.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/kustomize/api v0.20.0 // indirect sigs.k8s.io/kustomize/kyaml v0.20.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect diff --git a/plugins/scheduler-k3s/go.sum b/plugins/scheduler-k3s/go.sum index 46f0a044d..08511dbb7 100644 --- a/plugins/scheduler-k3s/go.sum +++ b/plugins/scheduler-k3s/go.sum @@ -89,6 +89,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fluxcd/pkg/kustomize v1.18.0 h1:wWK+qYwmBmba3N3VAqZ9ijnfVGGaIjcaHWo033URZTw= +github.com/fluxcd/pkg/kustomize v1.18.0/go.mod h1:Ij9722MdWIE6B1EPg2ZJUf6npycgfRfN4Lohi7D/Kic= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index da4cd008c..8eb42d4a8 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -14,6 +14,7 @@ import ( "unicode" "github.com/dokku/dokku/plugins/common" + "github.com/fluxcd/pkg/kustomize/filesys" "github.com/gofrs/flock" "gopkg.in/yaml.v3" "helm.sh/helm/v3/pkg/action" @@ -21,9 +22,13 @@ import ( "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/kube" + "helm.sh/helm/v3/pkg/postrender" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/storage/driver" + "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/types" ) var DevNullPrinter = func(format string, v ...interface{}) {} @@ -337,16 +342,6 @@ func (h *HelmAgent) ListRevisions(input ListRevisionsInput) ([]Release, error) { return releases, nil } -type DebugRenderer struct { -} - -func (p *DebugRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { - for _, line := range strings.Split(renderedManifests.String(), "\n") { - common.LogWarn(line) - } - return renderedManifests, nil -} - func (h *HelmAgent) UpgradeChart(ctx context.Context, input ChartInput) error { namespace := input.Namespace if namespace == "" { @@ -417,3 +412,77 @@ func (h *HelmAgent) UninstallChart(releaseName string) error { return nil } + +type DebugRenderer struct { + Renderer *postrender.PostRenderer +} + +func (p *DebugRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { + if p.Renderer != nil { + var err error + renderer := *p.Renderer + renderedManifests, err = renderer.Run(renderedManifests) + if err != nil { + return nil, err + } + } + + for _, line := range strings.Split(renderedManifests.String(), "\n") { + common.LogWarn(line) + } + return renderedManifests, nil +} + +type KustomizeRenderer struct { + Renderer *postrender.PostRenderer + KustomizeRootPath string +} + +func (p *KustomizeRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { + if p.KustomizeRootPath == "" { + return nil, nil + } + + if !common.DirectoryExists(p.KustomizeRootPath) { + return nil, nil + } + + fs, err := filesys.MakeFsOnDiskSecureBuild(p.KustomizeRootPath) + if err != nil { + return nil, fmt.Errorf("Error creating filesystem: %w", err) + } + + var kfile string + for _, f := range konfig.RecognizedKustomizationFileNames() { + if kf := filepath.Join(p.KustomizeRootPath, f); fs.Exists(kf) { + kfile = kf + break + } + } + if kfile == "" { + return nil, fmt.Errorf("%s not found", konfig.DefaultKustomizationFileName()) + } + + renderedYamlPath := filepath.Join(p.KustomizeRootPath, "rendered.yaml") + if err := os.WriteFile(renderedYamlPath, renderedManifests.Bytes(), 0644); err != nil { + return nil, fmt.Errorf("Error writing rendered.yaml: %w", err) + } + + buildOptions := &krusty.Options{ + LoadRestrictions: types.LoadRestrictionsNone, + PluginConfig: types.DisabledPluginConfig(), + } + + k := krusty.MakeKustomizer(buildOptions) + m, err := k.Run(fs, p.KustomizeRootPath) + if err != nil { + return nil, err + } + + resources, err := m.AsYaml() + if err != nil { + return nil, err + } + + return bytes.NewBuffer(resources), nil +} From e0617b209d0c93feb3464f5aee810df258c74a10 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 19 Jul 2025 00:05:44 -0400 Subject: [PATCH 72/97] feat: extract kustomize directory from repo during deploys --- plugins/scheduler-k3s/Makefile | 2 +- plugins/scheduler-k3s/functions.go | 13 +++ .../scheduler-k3s/src/triggers/triggers.go | 7 ++ plugins/scheduler-k3s/triggers.go | 80 +++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/Makefile b/plugins/scheduler-k3s/Makefile index 449103c2c..c0c9165bd 100644 --- a/plugins/scheduler-k3s/Makefile +++ b/plugins/scheduler-k3s/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/annotations:set subcommands/autoscaling-auth:set subcommands/autoscaling-auth:report subcommands/cluster-add subcommands/cluster-list subcommands/cluster-remove subcommands/ensure-charts subcommands/initialize subcommands/labels:set subcommands/report subcommands/set subcommands/show-kubeconfig subcommands/uninstall -TRIGGERS = triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-config triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop +TRIGGERS = triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-config triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = scheduler-k3s diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index a7ec92f92..472c06025 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1494,6 +1494,19 @@ func getStartCommand(input StartCommandInput) (StartCommandOutput, error) { }, nil } +func hasKustomizeRootPath(appName string) bool { + kustomizeRootPath := getComputedKustomizeRootPath(appName) + if common.DirectoryExists(fmt.Sprintf("%s.%s.missing", kustomizeRootPath, os.Getenv("DOKKU_PID"))) { + return false + } + + if common.DirectoryExists(fmt.Sprintf("%s.%s", kustomizeRootPath, os.Getenv("DOKKU_PID"))) { + return true + } + + return common.DirectoryExists(kustomizeRootPath) +} + func installHelmCharts(ctx context.Context, clientset KubernetesClient, shouldInstall func(HelmChart) bool) error { for _, repo := range HelmRepositories { helmAgent, err := NewHelmAgent("default", DeployLogPrinter) diff --git a/plugins/scheduler-k3s/src/triggers/triggers.go b/plugins/scheduler-k3s/src/triggers/triggers.go index 4092da096..27b6bdc98 100644 --- a/plugins/scheduler-k3s/src/triggers/triggers.go +++ b/plugins/scheduler-k3s/src/triggers/triggers.go @@ -22,6 +22,13 @@ func main() { var err error switch trigger { + case "core-post-deploy": + appName := flag.Arg(0) + err = scheduler_k3s.TriggerCorePostDeploy(appName) + case "core-post-extract": + appName := flag.Arg(0) + sourceWorkDir := flag.Arg(1) + err = scheduler_k3s.TriggerCorePostExtract(appName, sourceWorkDir) case "install": err = scheduler_k3s.TriggerInstall() case "post-app-clone-setup": diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index a228ac048..5fb53b6f1 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -11,6 +11,7 @@ import ( "io" "os" "os/signal" + "path" "path/filepath" "sort" "strconv" @@ -32,6 +33,85 @@ import ( "k8s.io/kubernetes/pkg/client/conditions" ) +// TriggerCorePostDeploy moves a configured kustomize root path to be in the app root dir +func TriggerCorePostDeploy(appName string) error { + existingKustomizeRootPath := getComputedKustomizeRootPath(appName) + processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", existingKustomizeRootPath, os.Getenv("DOKKU_PID")) + if common.DirectoryExists(processSpecificKustomizeRootPath) { + if err := os.Rename(processSpecificKustomizeRootPath, existingKustomizeRootPath); err != nil { + return err + } + } else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) { + if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)); err != nil { + return err + } + + if common.DirectoryExists(existingKustomizeRootPath) { + if err := os.Remove(existingKustomizeRootPath); err != nil { + return err + } + } + } + + return nil +} + +// TriggerCorePostExtract moves a configured kustomize root path to be in the app root dir +func TriggerCorePostExtract(appName string, sourceWorkDir string) error { + kustomizeRootPath := getComputedKustomizeRootPath(appName) + if kustomizeRootPath == "" { + return nil + } + + directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "scheduler-k3s", appName) + existingKustomizeDirectory := filepath.Join(directory, "kustomization") + files, err := filepath.Glob(fmt.Sprintf("%s.*", existingKustomizeDirectory)) + if err != nil { + return err + } + for _, f := range files { + if err := os.Remove(f); err != nil { + return err + } + } + + processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", kustomizeRootPath, os.Getenv("DOKKU_PID")) + results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ + Trigger: "git-get-property", + Args: []string{appName, "source-image"}, + }) + appSourceImage := results.StdoutContents() + + if appSourceImage == "" { + repoDefaultKustomizeRootPath := path.Join(sourceWorkDir, "kustomization") + repoKustomizeRootPath := path.Join(sourceWorkDir, kustomizeRootPath) + if !common.DirectoryExists(repoKustomizeRootPath) { + if kustomizeRootPath != "kustomization" && common.DirectoryExists(repoDefaultKustomizeRootPath) { + if err := os.RemoveAll(repoDefaultKustomizeRootPath); err != nil { + return fmt.Errorf("Unable to remove existing kustomize directory: %s", err.Error()) + } + } + return common.TouchDir(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) + } + + if err := common.Copy(repoKustomizeRootPath, processSpecificKustomizeRootPath); err != nil { + return fmt.Errorf("Unable to extract kustomize root path: %s", err.Error()) + } + + if kustomizeRootPath != "kustomization" { + if err := common.Copy(repoKustomizeRootPath, repoDefaultKustomizeRootPath); err != nil { + return fmt.Errorf("Unable to move kustomize root path into place: %s", err.Error()) + } + } + } else { + if err := common.CopyDirFromImage(appName, appSourceImage, kustomizeRootPath, processSpecificKustomizeRootPath); err != nil { + return common.TouchDir(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) + } + } + + return nil +} + // TriggerInstall runs the install step for the scheduler-k3s plugin func TriggerInstall() error { if err := common.PropertySetup("scheduler-k3s"); err != nil { From 04917b72d43335802d262d7fa0e24ad99b5a26f9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 19 Jul 2025 00:13:38 -0400 Subject: [PATCH 73/97] fix: ensure the directories are deleted --- plugins/scheduler-k3s/triggers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 5fb53b6f1..4f3366c51 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -42,12 +42,12 @@ func TriggerCorePostDeploy(appName string) error { return err } } else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) { - if err := os.Remove(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)); err != nil { + if err := os.RemoveAll(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)); err != nil { return err } if common.DirectoryExists(existingKustomizeRootPath) { - if err := os.Remove(existingKustomizeRootPath); err != nil { + if err := os.RemoveAll(existingKustomizeRootPath); err != nil { return err } } From e83ba595aadf71ffa47e5994027e7e918bed6916 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 19 Jul 2025 00:37:46 -0400 Subject: [PATCH 74/97] fix: correct issues with passing a KustomizeRenderer --- plugins/scheduler-k3s/helm.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index 8eb42d4a8..0a0310332 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -414,17 +414,13 @@ func (h *HelmAgent) UninstallChart(releaseName string) error { } type DebugRenderer struct { - Renderer *postrender.PostRenderer + Renderer postrender.PostRenderer } func (p *DebugRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { - if p.Renderer != nil { - var err error - renderer := *p.Renderer - renderedManifests, err = renderer.Run(renderedManifests) - if err != nil { - return nil, err - } + renderedManifests, err := p.Renderer.Run(renderedManifests) + if err != nil { + return nil, err } for _, line := range strings.Split(renderedManifests.String(), "\n") { @@ -434,7 +430,6 @@ func (p *DebugRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, err } type KustomizeRenderer struct { - Renderer *postrender.PostRenderer KustomizeRootPath string } From 272327af89c91888837c10cf03bea0fc1f11fc35 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sat, 19 Jul 2025 00:38:06 -0400 Subject: [PATCH 75/97] chore: add docs to the ChartInput type --- plugins/scheduler-k3s/helm.go | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index 0a0310332..f40502778 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -51,16 +51,34 @@ var DeployLogPrinter = func(format string, v ...interface{}) { } } +// ChartInput is the input for the InstallOrUpgradeChart function type ChartInput struct { - ChartPath string - Namespace string - ReleaseName string - RepoURL string + // ChartPath is the path to the chart to install or upgrade + ChartPath string + + // Namespace is the namespace to install or upgrade the chart in + Namespace string + + // ReleaseName is the name of the release to install or upgrade + ReleaseName string + + // RepoURL is the URL of the chart repository to install or upgrade the chart from + RepoURL string + + // RollbackOnFailure is whether to rollback on failure RollbackOnFailure bool - Timeout time.Duration - Wait bool - Version string - Values map[string]interface{} + + // Timeout is the timeout for the install or upgrade + Timeout time.Duration + + // Wait is whether to wait for the install or upgrade to complete + Wait bool + + // Version is the version of the chart to install or upgrade + Version string + + // Values is the values to pass to the chart + Values map[string]interface{} } type Release struct { From d0ffd7d9f97a9d50156613df07157cef55910eb4 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 00:04:35 -0400 Subject: [PATCH 76/97] feat: stub out kustomize renderer The process-specific kustomize root path is not currently set, but will be soon. --- plugins/scheduler-k3s/functions.go | 4 ++++ plugins/scheduler-k3s/helm.go | 23 +++++++++++++++++++++-- plugins/scheduler-k3s/triggers.go | 6 ++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 472c06025..2fe549f6e 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1494,6 +1494,10 @@ func getStartCommand(input StartCommandInput) (StartCommandOutput, error) { }, nil } +func getProcessSpecificKustomizeRootPath(appName string) string { + return "" +} + func hasKustomizeRootPath(appName string) bool { kustomizeRootPath := getComputedKustomizeRootPath(appName) if common.DirectoryExists(fmt.Sprintf("%s.%s.missing", kustomizeRootPath, os.Getenv("DOKKU_PID"))) { diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index f40502778..4f855c13f 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -56,6 +56,9 @@ type ChartInput struct { // ChartPath is the path to the chart to install or upgrade ChartPath string + // KustomizeRootPath is the path to the kustomize root path to use + KustomizeRootPath string + // Namespace is the namespace to install or upgrade the chart in Namespace string @@ -260,6 +263,10 @@ func (h *HelmAgent) InstallChart(ctx context.Context, input ChartInput) error { input.Values = map[string]interface{}{} } + kustomizeRenderer := KustomizeRenderer{ + KustomizeRootPath: input.KustomizeRootPath, + } + client := action.NewInstall(h.Configuration) client.Atomic = false client.ChartPathOptions = action.ChartPathOptions{} @@ -267,7 +274,11 @@ func (h *HelmAgent) InstallChart(ctx context.Context, input ChartInput) error { client.DryRun = false if os.Getenv("DOKKU_TRACE") == "1" { client.DryRun = true - client.PostRenderer = &DebugRenderer{} + client.PostRenderer = &DebugRenderer{ + Renderer: &kustomizeRenderer, + } + } else { + client.PostRenderer = &kustomizeRenderer } client.Namespace = namespace client.ReleaseName = input.ReleaseName @@ -376,6 +387,10 @@ func (h *HelmAgent) UpgradeChart(ctx context.Context, input ChartInput) error { input.Values = map[string]interface{}{} } + kustomizeRenderer := KustomizeRenderer{ + KustomizeRootPath: input.KustomizeRootPath, + } + client := action.NewUpgrade(h.Configuration) client.Atomic = input.RollbackOnFailure client.ChartPathOptions = action.ChartPathOptions{} @@ -383,7 +398,11 @@ func (h *HelmAgent) UpgradeChart(ctx context.Context, input ChartInput) error { client.MaxHistory = 10 if os.Getenv("DOKKU_TRACE") == "1" { client.DryRun = true - client.PostRenderer = &DebugRenderer{} + client.PostRenderer = &DebugRenderer{ + Renderer: &kustomizeRenderer, + } + } else { + client.PostRenderer = &kustomizeRenderer } client.Namespace = namespace client.Timeout = input.Timeout diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 4f3366c51..c1b08d905 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -784,9 +784,15 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e } } + kustomizeRootPath := "" + if hasKustomizeRootPath(appName) { + kustomizeRootPath = getProcessSpecificKustomizeRootPath(appName) + } + common.LogInfo2(fmt.Sprintf("Installing %s", appName)) err = helmAgent.InstallOrUpgradeChart(ctx, ChartInput{ ChartPath: chartPath, + KustomizeRootPath: kustomizeRootPath, Namespace: namespace, ReleaseName: appName, RollbackOnFailure: allowRollbacks, From fd6e56cc5fbac8c6cbee00e5c9422d0e8ca7f0bc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 00:11:25 -0400 Subject: [PATCH 77/97] fix: correct kustomize root path check --- plugins/scheduler-k3s/functions.go | 16 +++++++++++----- plugins/scheduler-k3s/triggers.go | 7 +++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 2fe549f6e..76f5991d7 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1190,6 +1190,11 @@ func getGlobalLetsencryptEmailStag() string { return common.PropertyGetDefault("scheduler-k3s", "--global", "letsencrypt-email-stag", "") } +func getKustomizeDirectory(appName string) string { + directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "scheduler-k3s", appName) + return filepath.Join(directory, "kustomization") +} + func getKustomizeRootPath(appName string) string { return common.PropertyGetDefault("scheduler-k3s", appName, "kustomize-root-path", "") } @@ -1498,17 +1503,18 @@ func getProcessSpecificKustomizeRootPath(appName string) string { return "" } -func hasKustomizeRootPath(appName string) bool { - kustomizeRootPath := getComputedKustomizeRootPath(appName) - if common.DirectoryExists(fmt.Sprintf("%s.%s.missing", kustomizeRootPath, os.Getenv("DOKKU_PID"))) { +func hasKustomizeDirectory(appName string) bool { + directory := getKustomizeDirectory(appName) + + if common.DirectoryExists(fmt.Sprintf("%s.%s.missing", directory, os.Getenv("DOKKU_PID"))) { return false } - if common.DirectoryExists(fmt.Sprintf("%s.%s", kustomizeRootPath, os.Getenv("DOKKU_PID"))) { + if common.DirectoryExists(fmt.Sprintf("%s.%s", directory, os.Getenv("DOKKU_PID"))) { return true } - return common.DirectoryExists(kustomizeRootPath) + return common.DirectoryExists(directory) } func installHelmCharts(ctx context.Context, clientset KubernetesClient, shouldInstall func(HelmChart) bool) error { diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index c1b08d905..f9fcd7817 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -63,8 +63,7 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { return nil } - directory := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "scheduler-k3s", appName) - existingKustomizeDirectory := filepath.Join(directory, "kustomization") + existingKustomizeDirectory := getKustomizeDirectory(appName) files, err := filepath.Glob(fmt.Sprintf("%s.*", existingKustomizeDirectory)) if err != nil { return err @@ -75,7 +74,7 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { } } - processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", kustomizeRootPath, os.Getenv("DOKKU_PID")) + processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", existingKustomizeDirectory, os.Getenv("DOKKU_PID")) results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "git-get-property", Args: []string{appName, "source-image"}, @@ -785,7 +784,7 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e } kustomizeRootPath := "" - if hasKustomizeRootPath(appName) { + if hasKustomizeDirectory(appName) { kustomizeRootPath = getProcessSpecificKustomizeRootPath(appName) } From 58c6b137b040157d7e9a123546ca8712c6369e64 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 01:48:15 -0400 Subject: [PATCH 78/97] refactor: use a common implementation to extract kustomize files --- plugins/scheduler-k3s/triggers.go | 58 +++++-------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index f9fcd7817..d2f4f06c6 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -11,7 +11,6 @@ import ( "io" "os" "os/signal" - "path" "path/filepath" "sort" "strconv" @@ -58,57 +57,16 @@ func TriggerCorePostDeploy(appName string) error { // TriggerCorePostExtract moves a configured kustomize root path to be in the app root dir func TriggerCorePostExtract(appName string, sourceWorkDir string) error { + destination := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "scheduler-k3s", appName) kustomizeRootPath := getComputedKustomizeRootPath(appName) - if kustomizeRootPath == "" { - return nil - } - - existingKustomizeDirectory := getKustomizeDirectory(appName) - files, err := filepath.Glob(fmt.Sprintf("%s.*", existingKustomizeDirectory)) - if err != nil { - return err - } - for _, f := range files { - if err := os.Remove(f); err != nil { - return err - } - } - - processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", existingKustomizeDirectory, os.Getenv("DOKKU_PID")) - results, _ := common.CallPlugnTrigger(common.PlugnTriggerInput{ - Trigger: "git-get-property", - Args: []string{appName, "source-image"}, + return common.CorePostExtract(common.CorePostExtractInput{ + AppName: appName, + SourceWorkDir: sourceWorkDir, + Destination: destination, + ToExtract: []common.CorePostExtractToExtract{ + {Path: kustomizeRootPath, IsDirectory: true, Destination: "kustomization"}, + }, }) - appSourceImage := results.StdoutContents() - - if appSourceImage == "" { - repoDefaultKustomizeRootPath := path.Join(sourceWorkDir, "kustomization") - repoKustomizeRootPath := path.Join(sourceWorkDir, kustomizeRootPath) - if !common.DirectoryExists(repoKustomizeRootPath) { - if kustomizeRootPath != "kustomization" && common.DirectoryExists(repoDefaultKustomizeRootPath) { - if err := os.RemoveAll(repoDefaultKustomizeRootPath); err != nil { - return fmt.Errorf("Unable to remove existing kustomize directory: %s", err.Error()) - } - } - return common.TouchDir(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) - } - - if err := common.Copy(repoKustomizeRootPath, processSpecificKustomizeRootPath); err != nil { - return fmt.Errorf("Unable to extract kustomize root path: %s", err.Error()) - } - - if kustomizeRootPath != "kustomization" { - if err := common.Copy(repoKustomizeRootPath, repoDefaultKustomizeRootPath); err != nil { - return fmt.Errorf("Unable to move kustomize root path into place: %s", err.Error()) - } - } - } else { - if err := common.CopyDirFromImage(appName, appSourceImage, kustomizeRootPath, processSpecificKustomizeRootPath); err != nil { - return common.TouchDir(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) - } - } - - return nil } // TriggerInstall runs the install step for the scheduler-k3s plugin From b24035c80acf4a8ddcd43c1a204afd0b7f69a98c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 01:58:44 -0400 Subject: [PATCH 79/97] refactor: use a common implementation to move files into place in golang core-post-deploy implementations --- plugins/scheduler-k3s/triggers.go | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index d2f4f06c6..a1a8edd70 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -34,30 +34,20 @@ import ( // TriggerCorePostDeploy moves a configured kustomize root path to be in the app root dir func TriggerCorePostDeploy(appName string) error { - existingKustomizeRootPath := getComputedKustomizeRootPath(appName) - processSpecificKustomizeRootPath := fmt.Sprintf("%s.%s", existingKustomizeRootPath, os.Getenv("DOKKU_PID")) - if common.DirectoryExists(processSpecificKustomizeRootPath) { - if err := os.Rename(processSpecificKustomizeRootPath, existingKustomizeRootPath); err != nil { - return err - } - } else if common.FileExists(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)) { - if err := os.RemoveAll(fmt.Sprintf("%s.missing", processSpecificKustomizeRootPath)); err != nil { - return err - } - - if common.DirectoryExists(existingKustomizeRootPath) { - if err := os.RemoveAll(existingKustomizeRootPath); err != nil { - return err - } - } - } + return common.CorePostDeploy(common.CorePostDeployInput{ + AppName: appName, + Destination: common.GetAppDataDirectory("scheduler-k3s", appName), + ExtractedPaths: []common.CorePostDeployPath{ + {Name: "kustomization", IsDirectory: true}, + }, + }) return nil } // TriggerCorePostExtract moves a configured kustomize root path to be in the app root dir func TriggerCorePostExtract(appName string, sourceWorkDir string) error { - destination := filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "data", "scheduler-k3s", appName) + destination := common.GetAppDataDirectory("scheduler-k3s", appName) kustomizeRootPath := getComputedKustomizeRootPath(appName) return common.CorePostExtract(common.CorePostExtractInput{ AppName: appName, From b6fdd4511caf793d6313bec44ae8f5d13ec1ae2d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 02:23:41 -0400 Subject: [PATCH 80/97] fix: add validation to input for common trigger functions --- plugins/scheduler-k3s/triggers.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index a1a8edd70..aa1e4f0ae 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -37,6 +37,7 @@ func TriggerCorePostDeploy(appName string) error { return common.CorePostDeploy(common.CorePostDeployInput{ AppName: appName, Destination: common.GetAppDataDirectory("scheduler-k3s", appName), + PluginName: "scheduler-k3s", ExtractedPaths: []common.CorePostDeployPath{ {Name: "kustomization", IsDirectory: true}, }, @@ -51,8 +52,9 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { kustomizeRootPath := getComputedKustomizeRootPath(appName) return common.CorePostExtract(common.CorePostExtractInput{ AppName: appName, - SourceWorkDir: sourceWorkDir, Destination: destination, + PluginName: "scheduler-k3s", + SourceWorkDir: sourceWorkDir, ToExtract: []common.CorePostExtractToExtract{ {Path: kustomizeRootPath, IsDirectory: true, Destination: "kustomization"}, }, From 563365b712d11ce43d857f66116e97bf26f18132 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 02:40:41 -0400 Subject: [PATCH 81/97] fix: add name to app-json trigger code and some formatting --- plugins/scheduler-k3s/triggers.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index aa1e4f0ae..d26056b99 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -39,7 +39,7 @@ func TriggerCorePostDeploy(appName string) error { Destination: common.GetAppDataDirectory("scheduler-k3s", appName), PluginName: "scheduler-k3s", ExtractedPaths: []common.CorePostDeployPath{ - {Name: "kustomization", IsDirectory: true}, + {Path: "kustomization", IsDirectory: true}, }, }) @@ -56,7 +56,12 @@ func TriggerCorePostExtract(appName string, sourceWorkDir string) error { PluginName: "scheduler-k3s", SourceWorkDir: sourceWorkDir, ToExtract: []common.CorePostExtractToExtract{ - {Path: kustomizeRootPath, IsDirectory: true, Destination: "kustomization"}, + { + Path: kustomizeRootPath, + IsDirectory: true, + Name: "config/kustomize", + Destination: "kustomization", + }, }, }) } From 0a86ffe01bde4343fe9a677cd20321165e95d486 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 03:14:51 -0400 Subject: [PATCH 82/97] fix: add missing scheduler-k3s directory --- plugins/scheduler-k3s/triggers.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index d26056b99..5c28d3c22 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -71,6 +71,11 @@ func TriggerInstall() error { if err := common.PropertySetup("scheduler-k3s"); err != nil { return fmt.Errorf("Unable to install the scheduler-k3s plugin: %s", err.Error()) } + + if err := common.SetupAppData("scheduler-k3s"); err != nil { + return err + } + return nil } From 3edddac0c16a0536df2b24c83544204e7ffdd247 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 03:28:16 -0400 Subject: [PATCH 83/97] fix: create the data directory --- plugins/scheduler-k3s/Makefile | 2 +- plugins/scheduler-k3s/src/triggers/triggers.go | 3 +++ plugins/scheduler-k3s/triggers.go | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/Makefile b/plugins/scheduler-k3s/Makefile index c0c9165bd..870e3ba05 100644 --- a/plugins/scheduler-k3s/Makefile +++ b/plugins/scheduler-k3s/Makefile @@ -1,5 +1,5 @@ SUBCOMMANDS = subcommands/annotations:set subcommands/autoscaling-auth:set subcommands/autoscaling-auth:report subcommands/cluster-add subcommands/cluster-list subcommands/cluster-remove subcommands/ensure-charts subcommands/initialize subcommands/labels:set subcommands/report subcommands/set subcommands/show-kubeconfig subcommands/uninstall -TRIGGERS = triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-config triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop +TRIGGERS = triggers/core-post-deploy triggers/core-post-extract triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-create triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-config triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop BUILD = commands subcommands triggers PLUGIN_NAME = scheduler-k3s diff --git a/plugins/scheduler-k3s/src/triggers/triggers.go b/plugins/scheduler-k3s/src/triggers/triggers.go index 27b6bdc98..692c62e1f 100644 --- a/plugins/scheduler-k3s/src/triggers/triggers.go +++ b/plugins/scheduler-k3s/src/triggers/triggers.go @@ -39,6 +39,9 @@ func main() { oldAppName := flag.Arg(0) newAppName := flag.Arg(1) err = scheduler_k3s.TriggerPostAppRenameSetup(oldAppName, newAppName) + case "post-create": + appName := flag.Arg(0) + err = scheduler_k3s.TriggerPostCreate(appName) case "post-delete": appName := flag.Arg(0) err = scheduler_k3s.TriggerPostDelete(appName) diff --git a/plugins/scheduler-k3s/triggers.go b/plugins/scheduler-k3s/triggers.go index 5c28d3c22..7c0a9f8c4 100644 --- a/plugins/scheduler-k3s/triggers.go +++ b/plugins/scheduler-k3s/triggers.go @@ -102,6 +102,11 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { return nil } +// TriggerPostCreate creates the scheduler-k3s data directory +func TriggerPostCreate(appName string) error { + return common.CreateAppDataDirectory("scheduler-k3s", appName) +} + // TriggerPostDelete destroys the scheduler-k3s data for a given app container func TriggerPostDelete(appName string) error { dataErr := common.RemoveAppDataDirectory("scheduler-k3s", appName) From 7595351b1d2d9126095e016f5f3e1d628e6aa57f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 15:42:41 -0400 Subject: [PATCH 84/97] debug: check on what pods are running in the cert-manager namespace --- tests/unit/scheduler-k3s-2.bats | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/scheduler-k3s-2.bats b/tests/unit/scheduler-k3s-2.bats index 60734ad07..4eec4820a 100644 --- a/tests/unit/scheduler-k3s-2.bats +++ b/tests/unit/scheduler-k3s-2.bats @@ -12,6 +12,7 @@ setup() { } teardown_() { + kubectl get pods -n cert-manager || true global_teardown dokku nginx:start uninstall_k3s || true From d33393d3fd7a456f9d73c9be11fb91424f78179f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 16:32:21 -0400 Subject: [PATCH 85/97] feat: debug failed global helm chart installations by checking all pod logs --- plugins/scheduler-k3s/functions.go | 19 +++++++++++++++++++ plugins/scheduler-k3s/k8s.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 76f5991d7..accb49d17 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1605,6 +1605,25 @@ func installHelmCharts(ctx context.Context, clientset KubernetesClient, shouldIn Wait: true, }) if err != nil { + chartPods, err := clientset.ListPods(ctx, ListPodsInput{ + Namespace: chart.Namespace, + }) + if err != nil { + return fmt.Errorf("Error listing chart pods: %w", err) + } + for _, pod := range chartPods { + logs, err := clientset.GetLogs(ctx, GetLogsInput{ + Name: pod.Name, + Namespace: pod.Namespace, + }) + if err != nil { + return fmt.Errorf("Error getting chart pod logs: %w", err) + } + common.LogExclaim(fmt.Sprintf("Logs for %s/%s", pod.Namespace, pod.Name)) + for _, log := range strings.Split(string(logs), "\n") { + common.LogInfo1(log) + } + } return fmt.Errorf("Error installing chart %s: %w", chart.ChartPath, err) } } diff --git a/plugins/scheduler-k3s/k8s.go b/plugins/scheduler-k3s/k8s.go index 494d5ab9a..491e9174a 100644 --- a/plugins/scheduler-k3s/k8s.go +++ b/plugins/scheduler-k3s/k8s.go @@ -416,6 +416,35 @@ func (k KubernetesClient) ExecCommand(ctx context.Context, input ExecCommandInpu }) } +// GetPodLogsInput contains all the information needed to get the logs for a Kubernetes pod +type GetLogsInput struct { + // Name is the Kubernetes pod name + Name string + + // Namespace is the Kubernetes namespace + Namespace string +} + +// GetLogs gets the logs for a Kubernetes pod +func (k KubernetesClient) GetLogs(ctx context.Context, input GetLogsInput) ([]byte, error) { + logOptions := corev1.PodLogOptions{} + + request := k.Client.CoreV1().Pods(input.Namespace).GetLogs(input.Name, &logOptions) + + readCloser, err := request.Stream(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get logs: %w", err) + } + defer readCloser.Close() + + bytes, err := io.ReadAll(readCloser) + if err != nil { + return nil, fmt.Errorf("failed to read logs: %w", err) + } + + return bytes, nil +} + // GetNodeInput contains all the information needed to get a Kubernetes node type GetNodeInput struct { // Name is the Kubernetes node name From 8f44a571581bd79bac3256335b46c2f4452e911c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 17:28:36 -0400 Subject: [PATCH 86/97] fix: return the initial manifests if there is no kustomize root path --- plugins/scheduler-k3s/helm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index 4f855c13f..98fedf630 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -472,11 +472,11 @@ type KustomizeRenderer struct { func (p *KustomizeRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { if p.KustomizeRootPath == "" { - return nil, nil + return renderedManifests, nil } if !common.DirectoryExists(p.KustomizeRootPath) { - return nil, nil + return renderedManifests, nil } fs, err := filesys.MakeFsOnDiskSecureBuild(p.KustomizeRootPath) From e7964ee99dc3d878856212003567383e61c8880c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 17:51:54 -0400 Subject: [PATCH 87/97] chore: remove debug code --- plugins/scheduler-k3s/functions.go | 19 ------------------- tests/unit/scheduler-k3s-2.bats | 1 - 2 files changed, 20 deletions(-) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index accb49d17..76f5991d7 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1605,25 +1605,6 @@ func installHelmCharts(ctx context.Context, clientset KubernetesClient, shouldIn Wait: true, }) if err != nil { - chartPods, err := clientset.ListPods(ctx, ListPodsInput{ - Namespace: chart.Namespace, - }) - if err != nil { - return fmt.Errorf("Error listing chart pods: %w", err) - } - for _, pod := range chartPods { - logs, err := clientset.GetLogs(ctx, GetLogsInput{ - Name: pod.Name, - Namespace: pod.Namespace, - }) - if err != nil { - return fmt.Errorf("Error getting chart pod logs: %w", err) - } - common.LogExclaim(fmt.Sprintf("Logs for %s/%s", pod.Namespace, pod.Name)) - for _, log := range strings.Split(string(logs), "\n") { - common.LogInfo1(log) - } - } return fmt.Errorf("Error installing chart %s: %w", chart.ChartPath, err) } } diff --git a/tests/unit/scheduler-k3s-2.bats b/tests/unit/scheduler-k3s-2.bats index 4eec4820a..60734ad07 100644 --- a/tests/unit/scheduler-k3s-2.bats +++ b/tests/unit/scheduler-k3s-2.bats @@ -12,7 +12,6 @@ setup() { } teardown_() { - kubectl get pods -n cert-manager || true global_teardown dokku nginx:start uninstall_k3s || true From 78bde7f817b914cf780e8e7657074dc4980a40ea Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:15:08 -0400 Subject: [PATCH 88/97] feat: add more context for warning about not having an autoscaling config --- plugins/scheduler-k3s/functions.go | 2 +- tests/unit/scheduler-k3s-3.bats | 63 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/unit/scheduler-k3s-3.bats diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 76f5991d7..10328d9d2 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -576,7 +576,7 @@ func getAutoscaling(input GetAutoscalingInput) (ProcessAutoscaling, error) { } if !ok { - common.LogWarn(fmt.Sprintf("No autoscaling config found for %s", input.AppName)) + common.LogWarn(fmt.Sprintf("No autoscaling config found for %s:%s", input.AppName, input.ProcessType)) return ProcessAutoscaling{}, nil } diff --git a/tests/unit/scheduler-k3s-3.bats b/tests/unit/scheduler-k3s-3.bats new file mode 100644 index 000000000..a27ab8c17 --- /dev/null +++ b/tests/unit/scheduler-k3s-3.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bats + +load test_helper + +TEST_APP="rdmtestapp" + +setup() { + uninstall_k3s || true + global_setup + dokku nginx:stop + export KUBECONFIG="/etc/rancher/k3s/k3s.yaml" +} + +teardown_() { + global_teardown + dokku nginx:start + uninstall_k3s || true +} + +@test "(scheduler-k3s) kustomize" { + if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then + skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN" + fi + + INGRESS_CLASS=nginx install_k3s + + run /bin/bash -c "dokku apps:create $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "kubectl get deployment $TEST_APP-web -o json | jq -r '.spec.replicas'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "3" +} + +inject_kustomization_yaml() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + mkdir -p "$APP_REPO_DIR/config/kustomize" + touch "$APP_REPO_DIR/config/kustomize/kustomization.yaml" + cat <"$APP_REPO_DIR/config/kustomize/kustomization.yaml" +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: +- patch: |- + - op: replace + path: /spec/replicas + value: 3 + target: + group: apps + version: v1 + kind: Deployment +EOF +} From 68f909b91d94cfcea75703511365e09c9acfe7d2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:16:49 -0400 Subject: [PATCH 89/97] fix: only process web-related templates when there is a web section embedded in the template --- plugins/scheduler-k3s/templates/chart/certificate.yaml | 4 ++-- plugins/scheduler-k3s/templates/chart/deployment.yaml | 10 +++++----- .../templates/chart/https-redirect-middleware.yaml | 4 ++-- .../scheduler-k3s/templates/chart/ingress-route.yaml | 4 ++-- plugins/scheduler-k3s/templates/chart/ingress.yaml | 6 +++--- .../templates/chart/keda-http-scaled-object.yaml | 6 +++--- .../chart/keda-interceptor-proxy-service.yaml | 6 +++--- .../templates/chart/keda-scaled-object.yaml | 4 ++-- plugins/scheduler-k3s/templates/chart/service.yaml | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/certificate.yaml b/plugins/scheduler-k3s/templates/chart/certificate.yaml index a16e03a8a..5065d77f8 100644 --- a/plugins/scheduler-k3s/templates/chart/certificate.yaml +++ b/plugins/scheduler-k3s/templates/chart/certificate.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/deployment.yaml b/plugins/scheduler-k3s/templates/chart/deployment.yaml index 6d06ec008..4ffa5ba29 100644 --- a/plugins/scheduler-k3s/templates/chart/deployment.yaml +++ b/plugins/scheduler-k3s/templates/chart/deployment.yaml @@ -1,11 +1,11 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} {{- $mappings := dict }} -{{- if eq $processName "web" }} +{{- if hasKey $config "web" }} {{- range $pdx, $port_map := $config.web.port_maps }} {{- $mappings := set $mappings $port_map.name "true" }} {{- end }} @@ -61,7 +61,7 @@ spec: {{- range $config.args }} - {{ . }} {{- end }} - {{- if eq $processName "web" }} + {{- if hasKey $config "web" }} env: - name: PORT value: "{{ $.Values.global.network.primary_port }}" @@ -73,7 +73,7 @@ spec: image: {{ $.Values.global.image.name }} imagePullPolicy: Always name: {{ $.Values.global.app_name }}-{{ $processName }} - {{- if eq $processName "web" }} + {{- if hasKey $config "web" }} ports: {{- range $pdx, $port_map := $config.web.port_maps }} {{- if and (eq $port_map.scheme "https") (hasKey $mappings (printf "http-80-%.0f" $port_map.container_port)) }} diff --git a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml index d81397a53..b4b6bc810 100644 --- a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml +++ b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml index 01bf6ff76..bdf492282 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress.yaml b/plugins/scheduler-k3s/templates/chart/ingress.yaml index b416c3eab..2349d0292 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress.yaml @@ -1,11 +1,11 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} {{- $routeToKeda := false }} -{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} +{{- if and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda")) }} {{- if and ($.Capabilities.APIVersions.Has "http.keda.sh/v1alpha1") (and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http")) }} {{- $routeToKeda = true }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml index be6890a96..2e1fea6db 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml @@ -1,11 +1,11 @@ {{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} -{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} +{{- if and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda")) }} {{- if and $config.autoscaling.http_trigger (eq $config.autoscaling.http_trigger.type "http") }} {{- $mappings := dict }} {{- range $pdx, $port_map := $config.web.port_maps }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml index 4c259c946..f7b76393b 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml @@ -1,11 +1,11 @@ {{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} -{{- if and (eq $processName "web") (and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda"))) }} +{{- if and $config.autoscaling (and $config.autoscaling.enabled (eq $config.autoscaling.type "keda")) }} --- kind: Service apiVersion: v1 diff --git a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml index b6633556f..e57e6a799 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml @@ -1,7 +1,7 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/service.yaml b/plugins/scheduler-k3s/templates/chart/service.yaml index 0b6a02417..f35967ab1 100644 --- a/plugins/scheduler-k3s/templates/chart/service.yaml +++ b/plugins/scheduler-k3s/templates/chart/service.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip cron jobs -{{- if and (hasKey $config "cron") (hasKey $config.cron "id") }} +# Skip processes that don't have a web section +{{- if not (hasKey $config "web") }} {{- continue }} {{- end }} From 2958f906e4b502d153decb76ef12e4263c3128db Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:28:40 -0400 Subject: [PATCH 90/97] chore: update and move comment in rendered template --- plugins/scheduler-k3s/templates/chart/certificate.yaml | 2 +- plugins/scheduler-k3s/templates/chart/deployment.yaml | 2 +- .../templates/chart/https-redirect-middleware.yaml | 2 +- plugins/scheduler-k3s/templates/chart/ingress-route.yaml | 2 +- plugins/scheduler-k3s/templates/chart/ingress.yaml | 2 +- .../scheduler-k3s/templates/chart/keda-http-scaled-object.yaml | 2 +- .../templates/chart/keda-interceptor-proxy-service.yaml | 2 +- plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml | 2 +- plugins/scheduler-k3s/templates/chart/service.yaml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/scheduler-k3s/templates/chart/certificate.yaml b/plugins/scheduler-k3s/templates/chart/certificate.yaml index 5065d77f8..077470ce8 100644 --- a/plugins/scheduler-k3s/templates/chart/certificate.yaml +++ b/plugins/scheduler-k3s/templates/chart/certificate.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/deployment.yaml b/plugins/scheduler-k3s/templates/chart/deployment.yaml index 4ffa5ba29..a741804d1 100644 --- a/plugins/scheduler-k3s/templates/chart/deployment.yaml +++ b/plugins/scheduler-k3s/templates/chart/deployment.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml index b4b6bc810..4a38d1291 100644 --- a/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml +++ b/plugins/scheduler-k3s/templates/chart/https-redirect-middleware.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml index bdf492282..fb137204e 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress-route.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress-route.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/ingress.yaml b/plugins/scheduler-k3s/templates/chart/ingress.yaml index 2349d0292..1b5cd1081 100644 --- a/plugins/scheduler-k3s/templates/chart/ingress.yaml +++ b/plugins/scheduler-k3s/templates/chart/ingress.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml index 2e1fea6db..5907ead1e 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-http-scaled-object.yaml @@ -1,7 +1,7 @@ {{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml index f7b76393b..ade8549a2 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-interceptor-proxy-service.yaml @@ -1,7 +1,7 @@ {{- if .Capabilities.APIVersions.Has "http.keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml index e57e6a799..3f37603ac 100644 --- a/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml +++ b/plugins/scheduler-k3s/templates/chart/keda-scaled-object.yaml @@ -1,7 +1,7 @@ {{- if .Capabilities.APIVersions.Has "keda.sh/v1alpha1" -}} {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} diff --git a/plugins/scheduler-k3s/templates/chart/service.yaml b/plugins/scheduler-k3s/templates/chart/service.yaml index f35967ab1..8980597da 100644 --- a/plugins/scheduler-k3s/templates/chart/service.yaml +++ b/plugins/scheduler-k3s/templates/chart/service.yaml @@ -1,6 +1,6 @@ {{- range $processName, $config := .Values.processes }} -# Skip processes that don't have a web section {{- if not (hasKey $config "web") }} +# Skip {{ $processName }} as it doesn't have a web section {{- continue }} {{- end }} From 553378085fae9b9688f9a495af65e183144f2efa Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:35:02 -0400 Subject: [PATCH 91/97] fix: ensure the rendered.yaml file is written into the secure filesystem --- plugins/scheduler-k3s/helm.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index 98fedf630..c886a8020 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -495,8 +495,7 @@ func (p *KustomizeRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, return nil, fmt.Errorf("%s not found", konfig.DefaultKustomizationFileName()) } - renderedYamlPath := filepath.Join(p.KustomizeRootPath, "rendered.yaml") - if err := os.WriteFile(renderedYamlPath, renderedManifests.Bytes(), 0644); err != nil { + if err := fs.WriteFile(filepath.Join(p.KustomizeRootPath, "rendered.yaml"), renderedManifests.Bytes()); err != nil { return nil, fmt.Errorf("Error writing rendered.yaml: %w", err) } From 476cc6045f7f1bb5b01589089741344595d7e6ea Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:46:37 -0400 Subject: [PATCH 92/97] fix: add resources to injected template --- tests/unit/scheduler-k3s-3.bats | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/scheduler-k3s-3.bats b/tests/unit/scheduler-k3s-3.bats index a27ab8c17..d683f6cb0 100644 --- a/tests/unit/scheduler-k3s-3.bats +++ b/tests/unit/scheduler-k3s-3.bats @@ -50,6 +50,8 @@ inject_kustomization_yaml() { cat <"$APP_REPO_DIR/config/kustomize/kustomization.yaml" apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization +resources: +- rendered.yaml patches: - patch: |- - op: replace From cd275138f33281c5822bfaeb86e20b4226ee7676 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:46:47 -0400 Subject: [PATCH 93/97] fix: actually use helper to inject template --- tests/unit/scheduler-k3s-3.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/scheduler-k3s-3.bats b/tests/unit/scheduler-k3s-3.bats index d683f6cb0..c6dba70c6 100644 --- a/tests/unit/scheduler-k3s-3.bats +++ b/tests/unit/scheduler-k3s-3.bats @@ -29,7 +29,7 @@ teardown_() { echo "status: $status" assert_success - run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP convert_to_dockerfile + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP inject_kustomization_yaml echo "output: $output" echo "status: $status" assert_success From 4aed599d8ccf1e047489c2a9fde432826823ee99 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:47:31 -0400 Subject: [PATCH 94/97] fix: do not run helm in dry-run mode when trace-mode is enabled --- plugins/scheduler-k3s/helm.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/scheduler-k3s/helm.go b/plugins/scheduler-k3s/helm.go index c886a8020..e9dd9f97e 100644 --- a/plugins/scheduler-k3s/helm.go +++ b/plugins/scheduler-k3s/helm.go @@ -273,7 +273,6 @@ func (h *HelmAgent) InstallChart(ctx context.Context, input ChartInput) error { client.CreateNamespace = true client.DryRun = false if os.Getenv("DOKKU_TRACE") == "1" { - client.DryRun = true client.PostRenderer = &DebugRenderer{ Renderer: &kustomizeRenderer, } @@ -397,7 +396,6 @@ func (h *HelmAgent) UpgradeChart(ctx context.Context, input ChartInput) error { client.CleanupOnFail = true client.MaxHistory = 10 if os.Getenv("DOKKU_TRACE") == "1" { - client.DryRun = true client.PostRenderer = &DebugRenderer{ Renderer: &kustomizeRenderer, } From dd7589323e673d0bc36d5028756d60f52e332f43 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:47:46 -0400 Subject: [PATCH 95/97] feat: implement fetching the process-specific kustomize root path --- plugins/scheduler-k3s/functions.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index 10328d9d2..16b9a1ab6 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -1500,7 +1500,17 @@ func getStartCommand(input StartCommandInput) (StartCommandOutput, error) { } func getProcessSpecificKustomizeRootPath(appName string) string { - return "" + if !hasKustomizeDirectory(appName) { + return "" + } + + directory := getKustomizeDirectory(appName) + processSpecificDirectory := fmt.Sprintf("%s.%s", directory, os.Getenv("DOKKU_PID")) + if common.DirectoryExists(processSpecificDirectory) { + return processSpecificDirectory + } + + return directory } func hasKustomizeDirectory(appName string) bool { From 245263d2ad10294febc97c7971cb3e6aa21b40a2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 20:58:18 -0400 Subject: [PATCH 96/97] docs: add documentation for Kustomize usage --- docs/deployment/schedulers/k3s.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/deployment/schedulers/k3s.md b/docs/deployment/schedulers/k3s.md index 372ce4d70..5056deb2e 100644 --- a/docs/deployment/schedulers/k3s.md +++ b/docs/deployment/schedulers/k3s.md @@ -141,6 +141,7 @@ The k3s plugin provides a number of settings that can be used to managed deploym | Name | Description | Global Default | |-----------------------|---------------------------------------------------|--------------------| | `deploy-timeout` | Controls when app deploys will timeout in seconds | `300s` | +| `kustomize-root-path` | Controls the folder context from the deployed repository used for Kustomize | `config/kustomize` | | `image-pull-secrets` | Name of a kubernetes secret used to auth against a registry | Contents of `~/.docker/config.json` from Dokku server | | `namespace` | Controls the namespace used for resource creation | `default` | | `rollback-on-failure` | Whether to rollback failed deploys | `false` | @@ -365,7 +366,6 @@ dokku scheduler-k3s:ensure-charts Once the chart is configured, an `http` trigger can be specified like so: - ```json { "formation": { @@ -461,6 +461,30 @@ dokku scheduler-k3s:autoscaling-auth:report node-js-app --include-metadata Datadog datadogSite: us5.datadoghq.com ``` +### Integrating Kustomize + +Dokku supports integration with [Kustomize](https://kustomize.io/) to further customize the generated helm charts for app deployments. For example, a `config/kustomize/kustomization.yaml` file with the following contents will override the scale for each process deployed to `3`: + +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- rendered.yaml +patches: +- patch: |- + - op: replace + path: /spec/replicas + value: 3 + target: + group: apps + version: v1 + kind: Deployment +``` + +When using Kustomize with Dokku, all Kustomize-files _must_ be placed in the `config/kustomize` folder, with a `kustomization.yaml` file being the entrypoint to Kustomize. Dokku will render the helm chart to a `rendered.yaml` file, and then execute Kustomize with the `config/kustomize` folder as the context. + +See the [Kustomize](https://kustomize.io/) website for more details on how to use Kustomize. + ### Using kubectl remotely > [!WARNING] @@ -515,7 +539,6 @@ dokku scheduler-k3s:set --global chart.cert-manager.version 1.13.3 > [!NOTE] > Properties follow dot-notation, and are expanded according to Helm's internal logic. See the [Helm documentation](https://helm.sh/docs/helm/helm_install/#helm-install) for `helm install` for further details. - To unset a chart property, omit the value from the `scheduler-k3s:set` call: ```shell From 344cb39a44e22d9cea6ee0b8ef2226020ba707d2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 23 Jul 2025 21:54:24 -0400 Subject: [PATCH 97/97] chore: drop support for Ubuntu 20.04 This is no longer supported upstream by Canonical. --- README.md | 2 +- bootstrap.sh | 8 ++++---- contrib/release-dokku | 2 +- docs/appendices/0.36.0-migration-guide.md | 4 ++++ docs/development/testing.md | 2 +- docs/getting-started/installation/index.md | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0406c29ce..982827e79 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Support us with a monthly donation and help us continue our activities. [[Become A fresh VM running any of the following operating systems: -- Ubuntu 20.04 / 22.04 / 24.04 (amd64/arm64) - Any currently supported release +- Ubuntu 22.04 / 24.04 (amd64/arm64) - Any currently supported release - Debian 11+ (amd64/arm64) An SSH keypair that can be used for application deployment. If this exists before installation, it will be automatically imported into dokku. diff --git a/bootstrap.sh b/bootstrap.sh index 21b65d3c1..8386c182f 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -3,7 +3,7 @@ set -eo pipefail [[ $TRACE ]] && set -x # A script to bootstrap dokku. -# It expects to be run on Ubuntu 20.04/22.04/24.04 via 'sudo` +# It expects to be run on Ubuntu 22.04/24.04 via 'sudo` # If installing a tag higher than 0.3.13, it may install dokku via a package (so long as the package is higher than 0.3.13) # It checks out the dokku source code from GitHub into ~/dokku and then runs 'make install' from dokku source. @@ -12,7 +12,7 @@ set -eo pipefail # That's good because it prevents our output overlapping with wget's. # It also means that we can't run a partially downloaded script. -SUPPORTED_VERSIONS="Debian [11, 12], Ubuntu [20.04, 22.04, 24.04]" +SUPPORTED_VERSIONS="Debian [11, 12], Ubuntu [22.04, 24.04]" log-fail() { declare desc="log fail formatter" @@ -160,7 +160,7 @@ install-dokku-from-deb-package() { local NO_INSTALL_RECOMMENDS=${DOKKU_NO_INSTALL_RECOMMENDS:=""} local OS_ID - if ! in-array "$DOKKU_DISTRO_VERSION" "20.04" "22.04" "24.04" "10" "11" "12"; then + if ! in-array "$DOKKU_DISTRO_VERSION" "22.04" "24.04" "10" "11" "12"; then log-fail "Unsupported Linux distribution. Only the following versions are supported: $SUPPORTED_VERSIONS" fi @@ -188,7 +188,7 @@ install-dokku-from-deb-package() { fi if [[ "$DOKKU_DISTRO" == "ubuntu" ]]; then - OS_IDS=("focal" "jammy" "noble") + OS_IDS=("jammy" "noble") if ! in-array "$OS_ID" "${OS_IDS[@]}"; then OS_ID="noble" fi diff --git a/contrib/release-dokku b/contrib/release-dokku index f71c39ee0..9b114fbcb 100755 --- a/contrib/release-dokku +++ b/contrib/release-dokku @@ -144,7 +144,7 @@ fn-publish-package() { fi done elif [[ "$DIST" == "ubuntu" ]]; then - OS_IDS=("focal" "jammy" "noble") + OS_IDS=("jammy" "noble") for OS_ID in "${OS_IDS[@]}"; do log-info "(release-dokku) pushing ${RELEASE_TYPE} to packagecloud.com/${REPOSITORY}/${DIST}" package_cloud push "${REPOSITORY}/${DIST}/${OS_ID}" "$PACKAGE_NAME" diff --git a/docs/appendices/0.36.0-migration-guide.md b/docs/appendices/0.36.0-migration-guide.md index 54f9b17eb..2caeca288 100644 --- a/docs/appendices/0.36.0-migration-guide.md +++ b/docs/appendices/0.36.0-migration-guide.md @@ -6,3 +6,7 @@ - Processes now default to a `30` second stop timeout. - The `domains:clear` command no longer runs domain setup after clearing the domains. Use `domains:reset` or run `domains:setup` manually to have the same effect. - The deprecated `DOKKU_DISABLE_ANSI_PREFIX_REMOVAL` setting has been removed. Dokku deploys no longer use shell escape codes to modify output, and thus deploy output may now contain `remote:` as a prefix in each lines output. + +## Deprecations + +Ubuntu 20.04 is now a deprecated installation target. The operating system will be considered EOL by Canonical in April 2025. Users are encouraged to upgrade to Ubuntu 24.04 or consider switching their instllation method to the [Docker-based installation method](/docs/getting-started/install/docker.md) to avoid any disruption in usage. diff --git a/docs/development/testing.md b/docs/development/testing.md index 669449a24..1daaca2be 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -13,7 +13,7 @@ All pull requests have tests run against them on [GitHub Actions](https://github If you wish to skip tests for a particular commit, e.g. documentation changes, you may add the `[ci skip]` designator to your commit message. Commits that _should_ be tested but have the above designator will not be merged. -While we do provide official packages for a variety of platforms, as our test suite currently runs on Ubuntu Noble 24.04, we only provide official installation support for that platform and the supported LTS releases of Ubuntu (currently 20.04, 22.04, and 24.04). +While we do provide official packages for a variety of platforms, as our test suite currently runs on Ubuntu Noble 24.04, we only provide official installation support for that platform and the supported LTS releases of Ubuntu (currently 22.04, and 24.04). ## Local Test Execution diff --git a/docs/getting-started/installation/index.md b/docs/getting-started/installation/index.md index babdf8525..b872ee6c0 100644 --- a/docs/getting-started/installation/index.md +++ b/docs/getting-started/installation/index.md @@ -9,7 +9,7 @@ Dokku is an extensible, open source Platform as a Service that runs on a single To start using Dokku, you'll need a system that meets the following minimum requirements: - A fresh installation of any of the following operating systems: - - [Ubuntu 20.04/22.04/24.04](https://www.ubuntu.com/download) + - [Ubuntu 22.04/24.04](https://www.ubuntu.com/download) - [Debian 11+ x64](https://www.debian.org/distrib/) - A server with one of the following architectures - AMD64 (alternatively known as `x86_64`), commonly used for Intel cloud servers