diff --git a/environment/docker/stats.go b/environment/docker/stats.go index 20b8474..cab9479 100644 --- a/environment/docker/stats.go +++ b/environment/docker/stats.go @@ -5,6 +5,7 @@ import ( "encoding/json" "io" "math" + "time" "emperror.dev/errors" "github.com/docker/docker/api/types" @@ -12,6 +13,23 @@ import ( "github.com/pterodactyl/wings/environment" ) +// Uptime returns the current uptime of the container in milliseconds. If the +// container is not currently running this will return 0. +func (e *Environment) Uptime(ctx context.Context) (int64, error) { + ins, err := e.client.ContainerInspect(ctx, e.Id) + if err != nil { + return 0, errors.Wrap(err, "environment: could not inspect container") + } + if !ins.State.Running { + return 0, nil + } + started, err := time.Parse(time.RFC3339, ins.State.StartedAt) + if err != nil { + return 0, errors.Wrap(err, "environment: failed to parse container start time") + } + return time.Since(started).Milliseconds(), nil +} + // Attach to the instance and then automatically emit an event whenever the resource usage for the // server process changes. func (e *Environment) pollResources(ctx context.Context) error { @@ -28,6 +46,11 @@ func (e *Environment) pollResources(ctx context.Context) error { } defer stats.Body.Close() + uptime, err := e.Uptime(ctx) + if err != nil { + e.log().WithField("error", err).Warn("failed to calculate container uptime") + } + dec := json.NewDecoder(stats.Body) for { select { @@ -50,7 +73,12 @@ func (e *Environment) pollResources(ctx context.Context) error { return nil } + if !v.PreRead.IsZero() { + uptime = uptime + v.Read.Sub(v.PreRead).Milliseconds() + } + st := environment.Stats{ + Uptime: uptime, Memory: calculateDockerMemory(v.MemoryStats), MemoryLimit: v.MemoryStats.Limit, CpuAbsolute: calculateDockerAbsoluteCpu(v.PreCPUStats, v.CPUStats), diff --git a/environment/environment.go b/environment/environment.go index f9568a2..ead4d25 100644 --- a/environment/environment.go +++ b/environment/environment.go @@ -104,4 +104,8 @@ type ProcessEnvironment interface { // handle this itself, but there are some scenarios where it is helpful for the server // to update the state externally (e.g. starting -> started). SetState(string) + + // Uptime returns the current environment uptime in milliseconds. This is + // the time that has passed since it was last started. + Uptime(ctx context.Context) (int64, error) } diff --git a/environment/stats.go b/environment/stats.go index 62ee8e6..5d5c872 100644 --- a/environment/stats.go +++ b/environment/stats.go @@ -1,8 +1,6 @@ package environment -// Defines the current resource usage for a given server instance. If a server is offline you -// should obviously expect memory and CPU usage to be 0. However, disk will always be returned -// since that is not dependent on the server being running to collect that data. +// Stats defines the current resource usage for a given server instance. type Stats struct { // The total amount of memory, in bytes, that this server instance is consuming. This is // calculated slightly differently than just using the raw Memory field that the stats @@ -19,12 +17,11 @@ type Stats struct { // does not take into account any limits on the server process itself. CpuAbsolute float64 `json:"cpu_absolute"` - // The current disk space being used by the server. This is cached to prevent slow lookup - // issues on frequent refreshes. - // Disk int64 `json:"disk_bytes"` - // Current network transmit in & out for a container. Network NetworkStats `json:"network"` + + // The current uptime of the container, in milliseconds. + Uptime int64 `json:"uptime"` } type NetworkStats struct { diff --git a/server/resources.go b/server/resources.go index ea25d06..c21723f 100644 --- a/server/resources.go +++ b/server/resources.go @@ -46,6 +46,7 @@ func (ru *ResourceUsage) Reset() { ru.Memory = 0 ru.CpuAbsolute = 0 + ru.Uptime = 0 ru.Network.TxBytes = 0 ru.Network.RxBytes = 0 }