2020-08-11 04:38:42 +00:00
|
|
|
package environment
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-08-28 03:05:07 +00:00
|
|
|
"github.com/apex/log"
|
2020-08-11 04:38:42 +00:00
|
|
|
"math"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Mount struct {
|
|
|
|
// In Docker environments this makes no difference, however in a non-Docker environment you
|
|
|
|
// should treat the "Default" mount as the root directory for the server. All other mounts
|
|
|
|
// are just in addition to that one, and generally things like shared maps or timezone data.
|
|
|
|
Default bool `json:"-"`
|
|
|
|
|
|
|
|
// The target path on the system. This is "/home/container" for all server's Default mount
|
|
|
|
// but in non-container environments you can likely ignore the target and just work with the
|
|
|
|
// source.
|
|
|
|
Target string `json:"target"`
|
|
|
|
|
|
|
|
// The directory from which the files will be read. In Docker environments this is the directory
|
|
|
|
// that we're mounting into the container at the Target location.
|
|
|
|
Source string `json:"source"`
|
|
|
|
|
2020-09-05 19:08:40 +00:00
|
|
|
// Whether or not the directory is being mounted as read-only. It is up to the environment to
|
2020-08-11 04:38:42 +00:00
|
|
|
// handle this value correctly and ensure security expectations are met with its usage.
|
|
|
|
ReadOnly bool `json:"read_only"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// The build settings for a given server that impact docker container creation and
|
|
|
|
// resource limits for a server instance.
|
|
|
|
type Limits struct {
|
|
|
|
// The total amount of memory in megabytes that this server is allowed to
|
|
|
|
// use on the host system.
|
|
|
|
MemoryLimit int64 `json:"memory_limit"`
|
|
|
|
|
|
|
|
// The amount of additional swap space to be provided to a container instance.
|
|
|
|
Swap int64 `json:"swap"`
|
|
|
|
|
|
|
|
// The relative weight for IO operations in a container. This is relative to other
|
|
|
|
// containers on the system and should be a value between 10 and 1000.
|
|
|
|
IoWeight uint16 `json:"io_weight"`
|
|
|
|
|
|
|
|
// The percentage of CPU that this instance is allowed to consume relative to
|
|
|
|
// the host. A value of 200% represents complete utilization of two cores. This
|
|
|
|
// should be a value between 1 and THREAD_COUNT * 100.
|
|
|
|
CpuLimit int64 `json:"cpu_limit"`
|
|
|
|
|
|
|
|
// The amount of disk space in megabytes that a server is allowed to use.
|
|
|
|
DiskSpace int64 `json:"disk_space"`
|
|
|
|
|
|
|
|
// Sets which CPU threads can be used by the docker instance.
|
|
|
|
Threads string `json:"threads"`
|
|
|
|
|
|
|
|
OOMDisabled bool `json:"oom_disabled"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Converts the CPU limit for a server build into a number that can be better understood
|
|
|
|
// by the Docker environment. If there is no limit set, return -1 which will indicate to
|
|
|
|
// Docker that it has unlimited CPU quota.
|
|
|
|
func (r *Limits) ConvertedCpuLimit() int64 {
|
|
|
|
if r.CpuLimit == 0 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.CpuLimit * 1000
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the hard limit for memory usage to be 5% more than the amount of memory assigned to
|
|
|
|
// the server. If the memory limit for the server is < 4G, use 10%, if less than 2G use
|
|
|
|
// 15%. This avoids unexpected crashes from processes like Java which run over the limit.
|
|
|
|
func (r *Limits) MemoryOverheadMultiplier() float64 {
|
|
|
|
if r.MemoryLimit <= 2048 {
|
|
|
|
return 1.15
|
|
|
|
} else if r.MemoryLimit <= 4096 {
|
|
|
|
return 1.10
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1.05
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Limits) BoundedMemoryLimit() int64 {
|
|
|
|
return int64(math.Round(float64(r.MemoryLimit) * r.MemoryOverheadMultiplier() * 1_000_000))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the amount of swap available as a total in bytes. This is returned as the amount
|
|
|
|
// of memory available to the server initially, PLUS the amount of additional swap to include
|
|
|
|
// which is the format used by Docker.
|
|
|
|
func (r *Limits) ConvertedSwap() int64 {
|
|
|
|
if r.Swap < 0 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
return (r.Swap * 1_000_000) + r.BoundedMemoryLimit()
|
|
|
|
}
|
|
|
|
|
|
|
|
type Variables map[string]interface{}
|
|
|
|
|
|
|
|
// Ugly hacky function to handle environment variables that get passed through as not-a-string
|
|
|
|
// from the Panel. Ideally we'd just say only pass strings, but that is a fragile idea and if a
|
|
|
|
// string wasn't passed through you'd cause a crash or the server to become unavailable. For now
|
|
|
|
// try to handle the most likely values from the JSON and hope for the best.
|
|
|
|
func (v Variables) Get(key string) string {
|
|
|
|
val, ok := v[key]
|
|
|
|
if !ok {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
switch val.(type) {
|
|
|
|
case int:
|
|
|
|
return strconv.Itoa(val.(int))
|
|
|
|
case int32:
|
|
|
|
return strconv.FormatInt(val.(int64), 10)
|
|
|
|
case int64:
|
|
|
|
return strconv.FormatInt(val.(int64), 10)
|
|
|
|
case float32:
|
|
|
|
return fmt.Sprintf("%f", val.(float32))
|
|
|
|
case float64:
|
|
|
|
return fmt.Sprintf("%f", val.(float64))
|
|
|
|
case bool:
|
|
|
|
return strconv.FormatBool(val.(bool))
|
2020-08-28 03:05:07 +00:00
|
|
|
case string:
|
|
|
|
return val.(string)
|
2020-08-11 04:38:42 +00:00
|
|
|
}
|
|
|
|
|
2020-08-28 03:05:07 +00:00
|
|
|
// TODO: I think we can add a check for val == nil and return an empty string for those
|
|
|
|
// and this warning should theoretically never happen?
|
|
|
|
log.Warn(fmt.Sprintf("failed to marshal environment variable \"%s\" of type %+v into string", key, val))
|
|
|
|
|
|
|
|
return ""
|
2020-08-11 04:38:42 +00:00
|
|
|
}
|