Files
dokku/plugins/logs/set.go
Jose Diaz-Gonzalez 52b26a3760 feat: add vector-cron-sink for scheduled cron task output
Scheduled cron task output previously reached only the `dokku` user's cron mail, and could not be redirected because `app.json` rejects bare shell operators in a cron `command`. Setting `vector-cron-sink` on an app or globally routes that output to a dedicated sink instead, on both the `docker-local` and `k3s` schedulers, which keeps log destinations under operator control rather than in a deployed repository. Cron events carry `dokku_app` and `dokku_cron_id` fields so a sink can give each task its own destination. This also fixes a `k3s` bug where configuring a global `vector-sink` silently removed the vector prometheus exporter sink.
2026-08-09 01:10:21 -04:00

109 lines
2.3 KiB
Go

package logs
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/dokku/dokku/plugins/common"
)
func validateSetValue(appName string, key string, value string) error {
if key == "max-size" {
return validateMaxSize(appName, value)
}
if key == "vector-image" {
return validateVectorImage(appName, value)
}
if key == "vector-networks" {
return validateVectorNetworks(appName, value)
}
if key == "vector-sink" || key == "vector-cron-sink" {
return validateVectorSink(appName, value)
}
return nil
}
func validateMaxSize(appName string, value string) error {
if value == "" {
return nil
}
if value == "unlimited" {
return nil
}
last := value[len(value)-1:]
if last != "k" && last != "m" && last != "g" {
return errors.New("Invalid max-size unit measure, value must end in any of [k, m, g]")
}
if len(value) < 2 {
return errors.New("Invalid max-size value, must be a number followed by a unit of measure [k, m, g]")
}
number := value[:len(value)-1]
if _, err := strconv.Atoi(number); err != nil {
return fmt.Errorf("Invalid max-size value, unable to convert number to int: %s", err.Error())
}
return nil
}
func validateVectorSink(appName string, value string) error {
if value == "" {
return nil
}
_, err := SinkValueToConfig(SinkValueToConfigInput{SinkValue: value})
if err != nil {
return err
}
return nil
}
func validateVectorImage(appName string, value string) error {
if appName != "--global" {
return errors.New("vector-image may only be set globally with --global")
}
return nil
}
func validateVectorNetworks(appName string, value string) error {
if appName != "--global" {
return errors.New("vector-networks may only be set globally with --global")
}
if value == "" {
return nil
}
for _, name := range strings.Split(value, ",") {
name = strings.TrimSpace(name)
if name == "" {
return errors.New("Invalid vector-networks value, empty entry in comma-separated list")
}
if name == "bridge" {
return errors.New("Invalid vector-networks value, \"bridge\" is not a valid entry for vector-networks")
}
result, err := common.CallExecCommand(common.ExecCommandInput{
Command: common.DockerBin(),
Args: []string{"network", "inspect", name},
})
if err != nil || result.ExitCode != 0 {
return fmt.Errorf("Network %q does not exist", name)
}
}
return nil
}