mirror of
https://github.com/dokku/dokku.git
synced 2026-08-29 10:08:53 +02:00
The generated vector config is a snapshot of the app list and their sink properties, but it was only ever written by `logs:set` and `logs:vector-start`. Renaming an app left a source filtering on a label no container carries and gave the new name no source at all, so the app kept a sink with nothing feeding it. Cloning produced the same result for the clone, and destroying an app left its source and sink behind, the latter still pointing at an endpoint decommissioned along with the app. The global relabel transform embeds app names directly in generated VRL, so a rename also left behind a branch naming an app that no longer existed. Every case was silent, and the only repair was an operator running `logs:vector-start`. The `post-app-clone-setup`, `post-app-rename-setup` and `post-delete` triggers now rewrite the config, warning rather than failing so that a config write cannot abort the app operation whose state it is derived from. Closes #8918.
171 lines
4.3 KiB
Go
171 lines
4.3 KiB
Go
package logs
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/dokku/dokku/plugins/common"
|
|
dockeroptions "github.com/dokku/dokku/plugins/docker-options"
|
|
)
|
|
|
|
// TriggerDockerArgsProcessDeploy outputs the logs plugin docker options for an app
|
|
func TriggerDockerArgsProcessDeploy(appName string) error {
|
|
stdin, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
allowedDrivers := map[string]bool{
|
|
"local": true,
|
|
"json-file": true,
|
|
}
|
|
|
|
ignoreMaxSize := false
|
|
options, err := dockeroptions.GetDockerOptionsForPhase(appName, "deploy")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hasDriverOpt := false
|
|
for _, option := range options {
|
|
if !strings.HasPrefix(option, "--log-driver=") {
|
|
continue
|
|
}
|
|
|
|
hasDriverOpt = true
|
|
logDriver := strings.TrimPrefix(option, "--log-driver=")
|
|
if !allowedDrivers[logDriver] {
|
|
ignoreMaxSize = true
|
|
}
|
|
break
|
|
}
|
|
|
|
if !hasDriverOpt {
|
|
result, _ := common.CallExecCommand(common.ExecCommandInput{
|
|
Command: common.DockerBin(),
|
|
Args: []string{"system", "info", "--format", "{{ .LoggingDriver }}"},
|
|
})
|
|
|
|
if !allowedDrivers[result.StdoutContents()] {
|
|
ignoreMaxSize = true
|
|
}
|
|
}
|
|
|
|
if !ignoreMaxSize {
|
|
maxSize := common.PropertyGet("logs", appName, "max-size")
|
|
if maxSize == "" {
|
|
maxSize = common.PropertyGetDefault("logs", "--global", "max-size", MaxSize)
|
|
}
|
|
|
|
if maxSize != "unlimited" {
|
|
fmt.Printf(" --log-opt=max-size=%s ", maxSize)
|
|
}
|
|
}
|
|
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
// TriggerInstall initializes log configuration
|
|
func TriggerInstall() error {
|
|
if err := common.PropertySetup("logs"); err != nil {
|
|
return fmt.Errorf("Unable to install the logs plugin: %s", err.Error())
|
|
}
|
|
|
|
if err := common.SetupAppData("logs"); err != nil {
|
|
return err
|
|
}
|
|
|
|
logDirectory := filepath.Join(common.MustGetEnv("DOKKU_LOGS_DIR"), "apps")
|
|
if err := os.MkdirAll(logDirectory, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
return common.SetPermissions(common.SetPermissionInput{
|
|
Filename: logDirectory,
|
|
Mode: os.FileMode(0755),
|
|
})
|
|
}
|
|
|
|
// TriggerLogsGetProperty writes the logs key to stdout for a given app container
|
|
func TriggerLogsGetProperty(appName string, key string) error {
|
|
if key != "max-size" {
|
|
return errors.New("Invalid logs property specified")
|
|
}
|
|
|
|
value := common.PropertyGet("logs", appName, "max-size")
|
|
if value == "" {
|
|
value = common.PropertyGetDefault("logs", "--global", "max-size", MaxSize)
|
|
}
|
|
|
|
fmt.Println(value)
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostAppCloneSetup creates new logs files and regenerates the vector
|
|
// config so that the clone gets a source of its own for the sink it inherited
|
|
func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error {
|
|
err := common.PropertyClone("logs", oldAppName, newAppName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.CloneAppData("logs", oldAppName, newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
regenerateVectorConfig()
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostAppRename removes the old app data
|
|
func TriggerPostAppRename(oldAppName string, newAppName string) error {
|
|
return common.MigrateAppDataDirectory("logs", oldAppName, newAppName)
|
|
}
|
|
|
|
// TriggerPostAppRenameSetup renames logs files and regenerates the vector config.
|
|
// Both app roots exist at this point, but the old app no longer has any logs
|
|
// properties, so it drops out of the generated config.
|
|
func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error {
|
|
if err := common.PropertyClone("logs", oldAppName, newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.PropertyDestroy("logs", oldAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.CloneAppData("logs", oldAppName, newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
regenerateVectorConfig()
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostCreate ensures apps have the correct data directory structure
|
|
func TriggerPostCreate(appName string) error {
|
|
return common.CreateAppDataDirectory("logs", appName)
|
|
}
|
|
|
|
// TriggerPostDelete destroys the logs property for a given app container and
|
|
// regenerates the vector config. The regeneration runs after the properties are
|
|
// destroyed so that the app drops out of the config even though its app root is
|
|
// only removed later in the destroy flow.
|
|
func TriggerPostDelete(appName string) error {
|
|
dataErr := common.RemoveAppDataDirectory("logs", appName)
|
|
propertyErr := common.PropertyDestroy("logs", appName)
|
|
|
|
regenerateVectorConfig()
|
|
|
|
if dataErr != nil {
|
|
return dataErr
|
|
}
|
|
|
|
return propertyErr
|
|
}
|