mirror of
https://github.com/dokku/dokku.git
synced 2026-05-18 05:05:46 +02:00
Adds a `docker-args-process-build` trigger to the resource plugin so limits set via `dokku resource:limit --process-type build APP` are applied during the build phase. Only `build.limit.*` properties are read - defaults do not inherit, since builds typically need more memory than runtime and a leaked tiny default would cause confusing OOM failures. Reservations are never applied at build time. Allowed flags are filtered per builder: herokuish gets cpu, memory, memory-swap, and nvidia-gpu; dockerfile gets memory and memory-swap; pack, nixpacks, railpack, lambda, and null emit nothing because their underlying CLIs do not accept docker run resource flags. The dockerfile builder whitelists the new memory flags and corrects pre-existing typos where `--ssh` mapped to `--platform` and `--ulimit` mapped to `--tag`.
218 lines
4.8 KiB
Go
218 lines
4.8 KiB
Go
package resource
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/dokku/dokku/plugins/common"
|
|
)
|
|
|
|
// TriggerDockerArgsProcessDeploy outputs the process-specific docker options
|
|
func TriggerDockerArgsProcessDeploy(appName string, processType string) error {
|
|
stdin, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if os.Getenv("DOKKU_OMIT_RESOURCE_ARGS") == "1" {
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
resources, err := common.PropertyGetAll("resource", appName)
|
|
if err != nil {
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
limits := make(map[string]string)
|
|
reservations := make(map[string]string)
|
|
|
|
validLimits := map[string]bool{
|
|
"cpu": true,
|
|
"nvidia-gpu": true,
|
|
"memory": true,
|
|
"memory-swap": true,
|
|
}
|
|
validReservations := map[string]bool{
|
|
"memory": true,
|
|
}
|
|
validPrefixes := []string{"_default_.", fmt.Sprintf("%s.", processType)}
|
|
for _, validPrefix := range validPrefixes {
|
|
for key, value := range resources {
|
|
if !strings.HasPrefix(key, validPrefix) {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(strings.TrimPrefix(key, validPrefix), ".", 2)
|
|
if parts[0] == "limit" {
|
|
if !validLimits[parts[1]] {
|
|
continue
|
|
}
|
|
|
|
if parts[1] == "cpu" {
|
|
parts[1] = "cpus"
|
|
}
|
|
|
|
if parts[1] == "nvidia-gpu" {
|
|
parts[1] = "gpus"
|
|
}
|
|
|
|
limits[parts[1]] = value
|
|
}
|
|
if parts[0] == "reserve" {
|
|
if !validReservations[parts[1]] {
|
|
continue
|
|
}
|
|
|
|
reservations[parts[1]] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
for key, value := range limits {
|
|
if value == "" {
|
|
continue
|
|
}
|
|
value = addMemorySuffixForDocker(key, value)
|
|
fmt.Printf(" --%s=%s ", key, value)
|
|
}
|
|
|
|
for key, value := range reservations {
|
|
if value == "" {
|
|
continue
|
|
}
|
|
value = addMemorySuffixForDocker(key, value)
|
|
fmt.Printf(" --%s-reservation=%s ", key, value)
|
|
}
|
|
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
// TriggerDockerArgsProcessBuild outputs build-phase docker options for a builder
|
|
func TriggerDockerArgsProcessBuild(appName string, builderType string) error {
|
|
stdin, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if os.Getenv("DOKKU_OMIT_RESOURCE_ARGS") == "1" {
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
allowed := validBuildLimitsForBuilder(builderType)
|
|
if len(allowed) == 0 {
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
resources, err := common.PropertyGetAll("resource", appName)
|
|
if err != nil {
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
limits := make(map[string]string)
|
|
prefix := "build.limit."
|
|
for key, value := range resources {
|
|
if !strings.HasPrefix(key, prefix) {
|
|
continue
|
|
}
|
|
resourceKey := strings.TrimPrefix(key, prefix)
|
|
if !allowed[resourceKey] {
|
|
continue
|
|
}
|
|
|
|
flagName := resourceKey
|
|
if resourceKey == "cpu" {
|
|
flagName = "cpus"
|
|
}
|
|
if resourceKey == "nvidia-gpu" {
|
|
flagName = "gpus"
|
|
}
|
|
limits[flagName] = value
|
|
}
|
|
|
|
for key, value := range limits {
|
|
if value == "" {
|
|
continue
|
|
}
|
|
value = addMemorySuffixForDocker(key, value)
|
|
fmt.Printf(" --%s=%s ", key, value)
|
|
}
|
|
|
|
fmt.Print(string(stdin))
|
|
return nil
|
|
}
|
|
|
|
// validBuildLimitsForBuilder returns the resource keys (in property-key form)
|
|
// that the named builder can apply during the build phase. An empty map means
|
|
// the builder does not support build-phase resource limits.
|
|
func validBuildLimitsForBuilder(builderType string) map[string]bool {
|
|
switch builderType {
|
|
case "herokuish":
|
|
return map[string]bool{
|
|
"cpu": true,
|
|
"memory": true,
|
|
"memory-swap": true,
|
|
"nvidia-gpu": true,
|
|
}
|
|
case "dockerfile":
|
|
return map[string]bool{
|
|
"memory": true,
|
|
"memory-swap": true,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TriggerInstall runs the install step for the resource plugin
|
|
func TriggerInstall() error {
|
|
if err := common.PropertySetup("resource"); err != nil {
|
|
return fmt.Errorf("Unable to install the resource plugin: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostAppCloneSetup creates new resource files
|
|
func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error {
|
|
err := common.PropertyClone("resource", oldAppName, newAppName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostAppRenameSetup renames resource files
|
|
func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error {
|
|
if err := common.PropertyClone("resource", oldAppName, newAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := common.PropertyDestroy("resource", oldAppName); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TriggerPostDelete destroys the resource property for a given app container
|
|
func TriggerPostDelete(appName string) error {
|
|
return common.PropertyDestroy("resource", appName)
|
|
}
|
|
|
|
// TriggerResourceGetProperty writes the resource key to stdout for a given app container
|
|
func TriggerResourceGetProperty(appName string, processType string, resourceType string, key string) error {
|
|
value, err := GetResourceValue(appName, processType, resourceType, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println(value)
|
|
return nil
|
|
}
|