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 }