2014-01-08 21:47:57 -05:00
package execdriver
import (
2015-03-05 12:55:14 -05:00
"encoding/json"
2014-01-13 19:18:46 -05:00
"errors"
2014-02-21 16:27:15 -05:00
"io"
2015-03-05 12:55:14 -05:00
"io/ioutil"
2014-02-21 16:27:15 -05:00
"os"
2014-01-09 18:04:45 -05:00
"os/exec"
2015-03-05 12:55:14 -05:00
"path/filepath"
"strconv"
2015-01-30 12:29:46 -05:00
"strings"
2015-01-07 17:43:04 -05:00
"time"
2015-02-11 14:21:38 -05:00
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/docker/pkg/ulimit"
"github.com/docker/libcontainer"
2015-03-05 12:55:14 -05:00
"github.com/docker/libcontainer/cgroups/fs"
"github.com/docker/libcontainer/configs"
2014-01-08 21:47:57 -05:00
)
2014-03-18 16:49:16 -04:00
// Context is a generic key value pair that allows
// arbatrary data to be sent
type Context map [ string ] string
2014-01-13 19:18:46 -05:00
var (
2015-01-07 21:02:08 -05:00
ErrNotRunning = errors . New ( "Container is not running" )
2014-01-13 21:36:59 -05:00
ErrWaitTimeoutReached = errors . New ( "Wait timeout reached" )
ErrDriverAlreadyRegistered = errors . New ( "A driver already registered this docker init function" )
ErrDriverNotFound = errors . New ( "The requested docker init has not been found" )
2014-01-13 19:18:46 -05:00
)
2014-08-26 18:44:00 -04:00
type StartCallback func ( * ProcessConfig , int )
2014-01-13 18:02:12 -05:00
2014-01-15 16:57:07 -05:00
// Driver specific information based on
// processes registered with the driver
2014-01-15 14:46:25 -05:00
type Info interface {
IsRunning ( ) bool
}
2014-02-21 16:27:15 -05:00
// Terminal in an interface for drivers to implement
// if they want to support Close and Resize calls from
// the core
type Terminal interface {
io . Closer
Resize ( height , width int ) error
}
type TtyTerminal interface {
2015-03-05 12:55:14 -05:00
Master ( ) libcontainer . Console
2014-02-21 16:27:15 -05:00
}
2014-10-08 13:03:57 -04:00
// ExitStatus provides exit reasons for a container.
type ExitStatus struct {
// The exit code with which the container exited.
ExitCode int
// Whether the container encountered an OOM.
OOMKilled bool
}
2014-01-09 18:04:45 -05:00
type Driver interface {
2014-10-30 19:06:54 -04:00
Run ( c * Command , pipes * Pipes , startCallback StartCallback ) ( ExitStatus , error ) // Run executes the process and blocks until the process exits and returns the exit code
2014-10-08 13:03:57 -04:00
// Exec executes the process in an existing container, blocks until the process exits and returns the exit code
2014-09-04 01:29:19 -04:00
Exec ( c * Command , processConfig * ProcessConfig , pipes * Pipes , startCallback StartCallback ) ( int , error )
2014-01-20 19:05:07 -05:00
Kill ( c * Command , sig int ) error
2014-05-21 17:06:18 -04:00
Pause ( c * Command ) error
Unpause ( c * Command ) error
2014-01-28 10:17:51 -05:00
Name ( ) string // Driver name
Info ( id string ) Info // "temporary" hack (until we move state from core to plugins)
GetPidsForContainer ( id string ) ( [ ] int , error ) // Returns a list of pids for the given container.
2014-03-26 02:48:16 -04:00
Terminate ( c * Command ) error // kill it with fire
2014-09-10 03:34:38 -04:00
Clean ( id string ) error // clean all traces of container exec
2015-01-07 17:43:04 -05:00
Stats ( id string ) ( * ResourceStats , error ) // Get resource stats for a running container
2014-01-09 18:04:45 -05:00
}
2014-01-08 21:47:57 -05:00
// Network settings of the container
type Network struct {
2014-05-02 17:17:31 -04:00
Interface * NetworkInterface ` json:"interface" ` // if interface is nil then networking is disabled
Mtu int ` json:"mtu" `
ContainerID string ` json:"container_id" ` // id of the container to join network.
HostNetworking bool ` json:"host_networking" `
2014-03-16 15:52:27 -04:00
}
2014-11-10 16:14:17 -05:00
// IPC settings of the container
type Ipc struct {
ContainerID string ` json:"container_id" ` // id of the container to join ipc.
HostIpc bool ` json:"host_ipc" `
}
2014-11-25 15:10:53 -05:00
// PID settings of the container
type Pid struct {
HostPid bool ` json:"host_pid" `
}
2014-03-16 15:52:27 -04:00
type NetworkInterface struct {
2015-01-08 18:03:19 -05:00
Gateway string ` json:"gateway" `
IPAddress string ` json:"ip" `
IPPrefixLen int ` json:"ip_prefix_len" `
MacAddress string ` json:"mac" `
Bridge string ` json:"bridge" `
GlobalIPv6Address string ` json:"global_ipv6" `
LinkLocalIPv6Address string ` json:"link_local_ipv6" `
GlobalIPv6PrefixLen int ` json:"global_ipv6_prefix_len" `
IPv6Gateway string ` json:"ipv6_gateway" `
2014-01-08 21:47:57 -05:00
}
2014-01-20 16:23:02 -05:00
type Resources struct {
2015-02-11 14:21:38 -05:00
Memory int64 ` json:"memory" `
MemorySwap int64 ` json:"memory_swap" `
CpuShares int64 ` json:"cpu_shares" `
Cpuset string ` json:"cpuset" `
Rlimits [ ] * ulimit . Rlimit ` json:"rlimits" `
2014-01-20 16:23:02 -05:00
}
2015-01-07 17:43:04 -05:00
type ResourceStats struct {
2015-03-05 12:55:14 -05:00
* libcontainer . Stats
2015-01-07 19:22:42 -05:00
Read time . Time ` json:"read" `
MemoryLimit int64 ` json:"memory_limit" `
SystemUsage uint64 ` json:"system_usage" `
2015-01-07 17:43:04 -05:00
}
2014-03-03 10:15:29 -05:00
type Mount struct {
Source string ` json:"source" `
Destination string ` json:"destination" `
Writable bool ` json:"writable" `
Private bool ` json:"private" `
2014-09-13 12:42:10 -04:00
Slave bool ` json:"slave" `
2014-03-03 10:15:29 -05:00
}
2014-08-26 18:05:37 -04:00
// Describes a process that will be run inside a container.
type ProcessConfig struct {
2014-01-20 19:05:07 -05:00
exec . Cmd ` json:"-" `
2014-01-10 14:44:35 -05:00
2014-08-26 18:44:00 -04:00
Privileged bool ` json:"privileged" `
User string ` json:"user" `
Tty bool ` json:"tty" `
Entrypoint string ` json:"entrypoint" `
Arguments [ ] string ` json:"arguments" `
Terminal Terminal ` json:"-" ` // standard or tty terminal
Console string ` json:"-" ` // dev/console path
2014-08-26 18:05:37 -04:00
}
// Process wrapps an os/exec.Cmd to add more metadata
type Command struct {
2014-09-29 18:40:26 -04:00
ID string ` json:"id" `
2015-01-13 16:52:51 -05:00
Rootfs string ` json:"rootfs" ` // root fs of the container
ReadonlyRootfs bool ` json:"readonly_rootfs" `
2014-09-29 18:40:26 -04:00
InitPath string ` json:"initpath" ` // dockerinit
WorkingDir string ` json:"working_dir" `
ConfigPath string ` json:"config_path" ` // this should be able to be removed when the lxc template is moved into the driver
Network * Network ` json:"network" `
2014-11-10 16:14:17 -05:00
Ipc * Ipc ` json:"ipc" `
2014-11-25 15:10:53 -05:00
Pid * Pid ` json:"pid" `
2014-09-29 18:40:26 -04:00
Resources * Resources ` json:"resources" `
Mounts [ ] Mount ` json:"mounts" `
2015-03-05 12:55:14 -05:00
AllowedDevices [ ] * configs . Device ` json:"allowed_devices" `
AutoCreatedDevices [ ] * configs . Device ` json:"autocreated_devices" `
2014-09-29 18:40:26 -04:00
CapAdd [ ] string ` json:"cap_add" `
CapDrop [ ] string ` json:"cap_drop" `
ContainerPid int ` json:"container_pid" ` // the pid for the process inside a container
ProcessConfig ProcessConfig ` json:"process_config" ` // Describes the init process of the container.
ProcessLabel string ` json:"process_label" `
MountLabel string ` json:"mount_label" `
LxcConfig [ ] string ` json:"lxc_config" `
2014-09-29 19:34:45 -04:00
AppArmorProfile string ` json:"apparmor_profile" `
2014-01-09 18:04:45 -05:00
}
2015-01-30 12:29:46 -05:00
2015-03-05 12:55:14 -05:00
func InitContainer ( c * Command ) * configs . Config {
2015-01-30 12:29:46 -05:00
container := template . New ( )
container . Hostname = getEnv ( "HOSTNAME" , c . ProcessConfig . Env )
container . Cgroups . Name = c . ID
container . Cgroups . AllowedDevices = c . AllowedDevices
2015-03-05 12:55:14 -05:00
container . Readonlyfs = c . ReadonlyRootfs
container . Devices = c . AutoCreatedDevices
container . Rootfs = c . Rootfs
container . Readonlyfs = c . ReadonlyRootfs
2015-01-30 12:29:46 -05:00
// check to see if we are running in ramdisk to disable pivot root
2015-03-05 12:55:14 -05:00
container . NoPivotRoot = os . Getenv ( "DOCKER_RAMDISK" ) != ""
2015-01-30 12:29:46 -05:00
return container
}
func getEnv ( key string , env [ ] string ) string {
for _ , pair := range env {
parts := strings . Split ( pair , "=" )
if parts [ 0 ] == key {
return parts [ 1 ]
}
}
return ""
}
2015-03-05 12:55:14 -05:00
func SetupCgroups ( container * configs . Config , c * Command ) error {
2015-01-30 12:29:46 -05:00
if c . Resources != nil {
container . Cgroups . CpuShares = c . Resources . CpuShares
container . Cgroups . Memory = c . Resources . Memory
container . Cgroups . MemoryReservation = c . Resources . Memory
container . Cgroups . MemorySwap = c . Resources . MemorySwap
container . Cgroups . CpusetCpus = c . Resources . Cpuset
}
return nil
}
2015-03-05 12:55:14 -05:00
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
func getNetworkInterfaceStats ( interfaceName string ) ( * libcontainer . NetworkInterface , error ) {
out := & libcontainer . NetworkInterface { Name : interfaceName }
// This can happen if the network runtime information is missing - possible if the
// container was created by an old version of libcontainer.
if interfaceName == "" {
return out , nil
}
type netStatsPair struct {
// Where to write the output.
Out * uint64
// The network stats file to read.
File string
}
// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
netStats := [ ] netStatsPair {
{ Out : & out . RxBytes , File : "tx_bytes" } ,
{ Out : & out . RxPackets , File : "tx_packets" } ,
{ Out : & out . RxErrors , File : "tx_errors" } ,
{ Out : & out . RxDropped , File : "tx_dropped" } ,
{ Out : & out . TxBytes , File : "rx_bytes" } ,
{ Out : & out . TxPackets , File : "rx_packets" } ,
{ Out : & out . TxErrors , File : "rx_errors" } ,
{ Out : & out . TxDropped , File : "rx_dropped" } ,
}
for _ , netStat := range netStats {
data , err := readSysfsNetworkStats ( interfaceName , netStat . File )
if err != nil {
return nil , err
2015-01-30 12:29:46 -05:00
}
2015-03-05 12:55:14 -05:00
* ( netStat . Out ) = data
}
return out , nil
}
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
func readSysfsNetworkStats ( ethInterface , statsFile string ) ( uint64 , error ) {
data , err := ioutil . ReadFile ( filepath . Join ( "/sys/class/net" , ethInterface , "statistics" , statsFile ) )
if err != nil {
return 0 , err
}
return strconv . ParseUint ( strings . TrimSpace ( string ( data ) ) , 10 , 64 )
}
func Stats ( containerDir string , containerMemoryLimit int64 , machineMemory int64 ) ( * ResourceStats , error ) {
f , err := os . Open ( filepath . Join ( containerDir , "state.json" ) )
if err != nil {
return nil , err
}
defer f . Close ( )
type network struct {
Type string
HostInterfaceName string
}
state := struct {
CgroupPaths map [ string ] string ` json:"cgroup_paths" `
Networks [ ] network
} { }
if err := json . NewDecoder ( f ) . Decode ( & state ) ; err != nil {
2015-01-30 12:29:46 -05:00
return nil , err
}
now := time . Now ( )
2015-03-05 12:55:14 -05:00
mgr := fs . Manager { Paths : state . CgroupPaths }
cstats , err := mgr . GetStats ( )
2015-01-30 12:29:46 -05:00
if err != nil {
return nil , err
}
2015-03-05 12:55:14 -05:00
stats := & libcontainer . Stats { CgroupStats : cstats }
2015-01-30 12:29:46 -05:00
// if the container does not have any memory limit specified set the
// limit to the machines memory
memoryLimit := containerMemoryLimit
if memoryLimit == 0 {
memoryLimit = machineMemory
}
2015-03-05 12:55:14 -05:00
for _ , iface := range state . Networks {
switch iface . Type {
case "veth" :
istats , err := getNetworkInterfaceStats ( iface . HostInterfaceName )
if err != nil {
return nil , err
}
stats . Interfaces = append ( stats . Interfaces , istats )
}
}
2015-01-30 12:29:46 -05:00
return & ResourceStats {
2015-03-05 12:55:14 -05:00
Stats : stats ,
Read : now ,
MemoryLimit : memoryLimit ,
2015-01-30 12:29:46 -05:00
} , nil
}