service.go
package platform
import "fmt"
// Service represents a Docker container to run on a server
type Service struct {
Name string
Image string
Command []string
Ports []Port
Volumes []Mount
Env map[string]string
Network string
Restart string
Healthcheck *Healthcheck
Resources *Resources
Logging *LogConfig
Privileged bool
}
// Healthcheck configures container health checking
type Healthcheck struct {
Cmd string // Command to run (e.g., "curl -f http://localhost/health")
Interval string // Time between checks (e.g., "30s")
Timeout string // Time to wait for check to complete (e.g., "10s")
Retries int // Consecutive failures before unhealthy
StartPeriod string // Grace period before health checks start (e.g., "5s")
}
// Resources configures CPU and memory limits
type Resources struct {
CPUs string // CPU limit (e.g., "0.5", "2")
Memory string // Memory limit (e.g., "512m", "1g")
}
// LogConfig configures container logging
type LogConfig struct {
Driver string // Log driver (e.g., "json-file", "syslog")
Options map[string]string // Driver-specific options
}
// Port maps a host port to a container port
type Port struct {
Host int
Container int
Bind string // Optional bind address (e.g., "127.0.0.1")
}
// Mount binds a host path to a container path
type Mount struct {
Source string
Target string
}
// Start runs a service container on the server
func (s *Server) Start(service *Service) error {
args := []string{"docker", "run", "-d", "--name", service.Name}
// Add restart policy
if service.Restart != "" {
args = append(args, "--restart", service.Restart)
}
// Add network
if service.Network != "" {
args = append(args, "--network", service.Network)
}
// Add ports
for _, p := range service.Ports {
portMap := fmt.Sprintf("%d:%d", p.Host, p.Container)
if p.Bind != "" {
portMap = p.Bind + ":" + portMap
}
args = append(args, "-p", portMap)
}
// Add volumes
for _, v := range service.Volumes {
args = append(args, "-v", v.Source+":"+v.Target)
}
// Add environment variables
for k, v := range service.Env {
args = append(args, "-e", k+"="+v)
}
// Add healthcheck
if h := service.Healthcheck; h != nil {
if h.Cmd != "" {
// shellEscape in SSH handles quoting
args = append(args, "--health-cmd", h.Cmd)
}
if h.Interval != "" {
args = append(args, "--health-interval", h.Interval)
}
if h.Timeout != "" {
args = append(args, "--health-timeout", h.Timeout)
}
if h.Retries > 0 {
args = append(args, "--health-retries", fmt.Sprintf("%d", h.Retries))
}
if h.StartPeriod != "" {
args = append(args, "--health-start-period", h.StartPeriod)
}
}
// Add resource limits
if r := service.Resources; r != nil {
if r.CPUs != "" {
args = append(args, "--cpus", r.CPUs)
}
if r.Memory != "" {
args = append(args, "--memory", r.Memory)
}
}
// Add logging configuration
if l := service.Logging; l != nil {
if l.Driver != "" {
args = append(args, "--log-driver", l.Driver)
}
for k, v := range l.Options {
args = append(args, "--log-opt", k+"="+v)
}
}
// Add privileged flag
if service.Privileged {
args = append(args, "--privileged")
}
// Add image
args = append(args, service.Image)
// Add command
args = append(args, service.Command...)
_, err := s.SSH(args...)
return err
}
// Stop kills and removes a container on the server
func (s *Server) Stop(name string) error {
_, err := s.SSH("docker", "rm", "-f", name)
return err
}