mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
refactor: api.Container to use the detailed ContainerJSON struct instead of Container (Summary)
This commit is contained in:
+63
-43
@@ -3,8 +3,9 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types"
|
||||
"regexp"
|
||||
"github.com/docker/go-units"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,42 +14,30 @@ const (
|
||||
LabelServiceName = "uncloud.service.name"
|
||||
LabelServiceMode = "uncloud.service.mode"
|
||||
LabelServicePorts = "uncloud.service.ports"
|
||||
|
||||
StateCreated = "created"
|
||||
StateDead = "dead"
|
||||
StateExited = "exited"
|
||||
StatePaused = "paused"
|
||||
StateRemoving = "removing"
|
||||
StateRestarting = "restarting"
|
||||
StateRunning = "running"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
types.Container
|
||||
}
|
||||
|
||||
func (c *Container) Name() string {
|
||||
return c.Names[0][1:] // Remove leading slash.
|
||||
types.ContainerJSON
|
||||
}
|
||||
|
||||
// ServiceID returns the ID of the service this container belongs to.
|
||||
func (c *Container) ServiceID() string {
|
||||
return c.Labels[LabelServiceID]
|
||||
return c.Config.Labels[LabelServiceID]
|
||||
}
|
||||
|
||||
// ServiceName returns the name of the service this container belongs to.
|
||||
func (c *Container) ServiceName() string {
|
||||
return c.Labels[LabelServiceName]
|
||||
return c.Config.Labels[LabelServiceName]
|
||||
}
|
||||
|
||||
// ServiceMode returns the replication mode of the service this container belongs to.
|
||||
func (c *Container) ServiceMode() string {
|
||||
return c.Labels[LabelServiceMode]
|
||||
return c.Config.Labels[LabelServiceMode]
|
||||
}
|
||||
|
||||
// ServicePorts returns the ports this container publishes as part of its service.
|
||||
func (c *Container) ServicePorts() ([]PortSpec, error) {
|
||||
encoded, ok := c.Labels[LabelServicePorts]
|
||||
encoded, ok := c.Config.Labels[LabelServicePorts]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -75,41 +64,72 @@ func (c *Container) ServiceSpec() ServiceSpec {
|
||||
return ServiceSpec{}
|
||||
}
|
||||
|
||||
// runningStatusRegex matches the status string of a running container.
|
||||
// - "Up 3 minutes (healthy)" -> groups: ["Up 3 minutes (healthy)", "healthy"]
|
||||
// - "Up 5 seconds" -> groups: ["Up 5 seconds", ""]
|
||||
// - "Up 2 hours (unhealthy)" -> groups: ["Up 2 hours (unhealthy)", "unhealthy"]
|
||||
// - "Up 1 minute (health: starting)" -> groups: ["Up 1 minute (health: starting)", "health: starting"]
|
||||
// - "Restarting (0) 5 seconds ago" -> no match
|
||||
// See https://github.com/moby/moby/blob/c130ce1f5d1e38b98a97044a39557de43bc0d58f/container/state.go#L77-L90
|
||||
// for more details on how the status string for a running container is formatted.
|
||||
var runningStatusRegex = regexp.MustCompile(`^Up [^(]+(?:\(([^)]+)\))?$`)
|
||||
|
||||
// Healthy determines if the container is running and healthy based on its status string.
|
||||
// Healthy determines if the container is running and healthy.
|
||||
// A running container with no health check configured is considered healthy.
|
||||
func (c *Container) Healthy() bool {
|
||||
if c.State != StateRunning {
|
||||
if !c.State.Running || c.State.Paused || c.State.Restarting {
|
||||
return false
|
||||
}
|
||||
|
||||
matches := runningStatusRegex.FindStringSubmatch(c.Status)
|
||||
// Not "Up" or invalid format.
|
||||
if matches == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// If there's no health status (no health check configured so no parentheses), container is considered healthy.
|
||||
if matches[1] == "" {
|
||||
// If there's no health status (no health check configured), container is considered healthy.
|
||||
if c.State.Health == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the health status in parentheses is "healthy", the container is considered healthy.
|
||||
return matches[1] == types.Healthy
|
||||
return c.State.Health.Status == types.Healthy
|
||||
}
|
||||
|
||||
// Stopped determines if the container is stopped and doesn't try to restart.
|
||||
func (c *Container) Stopped() bool {
|
||||
return c.State == StateCreated || c.State == StateDead || c.State == StateExited
|
||||
// HumanState returns a human-readable description of the container's state. Based on the Docker implementation:
|
||||
// https://github.com/moby/moby/blob/b343d235a0a1f30c8f05b1d651238e72158dc25d/container/state.go#L79-L113
|
||||
func (c *Container) HumanState() (string, error) {
|
||||
startedAt, err := time.Parse(time.RFC3339Nano, c.State.StartedAt)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse started time: %w", err)
|
||||
}
|
||||
finishedAt, err := time.Parse(time.RFC3339Nano, c.State.FinishedAt)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parse finished time: %w", err)
|
||||
}
|
||||
|
||||
if c.State.Running {
|
||||
if c.State.Paused {
|
||||
return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(startedAt))), nil
|
||||
}
|
||||
if c.State.Restarting {
|
||||
return fmt.Sprintf("Restarting (%d) %s ago",
|
||||
c.State.ExitCode, units.HumanDuration(time.Now().UTC().Sub(finishedAt))), nil
|
||||
}
|
||||
|
||||
if c.State.Health != nil {
|
||||
status := c.State.Health.Status
|
||||
if status == types.Starting {
|
||||
status = "health: " + status
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Up %s (%s)", units.HumanDuration(time.Now().UTC().Sub(startedAt)), status), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(startedAt))), nil
|
||||
}
|
||||
|
||||
if c.State.Status == "removing" {
|
||||
return "Removal In Progress", nil
|
||||
}
|
||||
|
||||
if c.State.Dead {
|
||||
return "Dead", nil
|
||||
}
|
||||
|
||||
if startedAt.IsZero() {
|
||||
return "Created", nil
|
||||
}
|
||||
|
||||
if finishedAt.IsZero() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Exited (%d) %s ago",
|
||||
c.State.ExitCode, units.HumanDuration(time.Now().UTC().Sub(finishedAt))), nil
|
||||
}
|
||||
|
||||
// ConflictingServicePorts returns a list of service ports that conflict with the given ports.
|
||||
|
||||
Reference in New Issue
Block a user