github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/daemon/execdriver/driver.go (about) 1 package execdriver 2 3 import ( 4 "encoding/json" 5 "errors" 6 "io" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/docker/docker/daemon/execdriver/native/template" 16 "github.com/docker/docker/pkg/ulimit" 17 "github.com/docker/libcontainer" 18 "github.com/docker/libcontainer/cgroups/fs" 19 "github.com/docker/libcontainer/configs" 20 ) 21 22 // Context is a generic key value pair that allows 23 // arbatrary data to be sent 24 type Context map[string]string 25 26 var ( 27 ErrNotRunning = errors.New("Container is not running") 28 ErrWaitTimeoutReached = errors.New("Wait timeout reached") 29 ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function") 30 ErrDriverNotFound = errors.New("The requested docker init has not been found") 31 ) 32 33 type StartCallback func(*ProcessConfig, int) 34 35 // Driver specific information based on 36 // processes registered with the driver 37 type Info interface { 38 IsRunning() bool 39 } 40 41 // Terminal in an interface for drivers to implement 42 // if they want to support Close and Resize calls from 43 // the core 44 type Terminal interface { 45 io.Closer 46 Resize(height, width int) error 47 } 48 49 type TtyTerminal interface { 50 Master() libcontainer.Console 51 } 52 53 // ExitStatus provides exit reasons for a container. 54 type ExitStatus struct { 55 // The exit code with which the container exited. 56 ExitCode int 57 58 // Whether the container encountered an OOM. 59 OOMKilled bool 60 } 61 62 type Driver interface { 63 Run(c *Command, pipes *Pipes, startCallback StartCallback) (ExitStatus, error) // Run executes the process and blocks until the process exits and returns the exit code 64 // Exec executes the process in an existing container, blocks until the process exits and returns the exit code 65 Exec(c *Command, processConfig *ProcessConfig, pipes *Pipes, startCallback StartCallback) (int, error) 66 Kill(c *Command, sig int) error 67 Pause(c *Command) error 68 Unpause(c *Command) error 69 Name() string // Driver name 70 Info(id string) Info // "temporary" hack (until we move state from core to plugins) 71 GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container. 72 Terminate(c *Command) error // kill it with fire 73 Clean(id string) error // clean all traces of container exec 74 Stats(id string) (*ResourceStats, error) // Get resource stats for a running container 75 } 76 77 // Network settings of the container 78 type Network struct { 79 Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled 80 Mtu int `json:"mtu"` 81 ContainerID string `json:"container_id"` // id of the container to join network. 82 HostNetworking bool `json:"host_networking"` 83 } 84 85 // IPC settings of the container 86 type Ipc struct { 87 ContainerID string `json:"container_id"` // id of the container to join ipc. 88 HostIpc bool `json:"host_ipc"` 89 } 90 91 // PID settings of the container 92 type Pid struct { 93 HostPid bool `json:"host_pid"` 94 } 95 96 type NetworkInterface struct { 97 Gateway string `json:"gateway"` 98 IPAddress string `json:"ip"` 99 IPPrefixLen int `json:"ip_prefix_len"` 100 MacAddress string `json:"mac"` 101 Bridge string `json:"bridge"` 102 GlobalIPv6Address string `json:"global_ipv6"` 103 LinkLocalIPv6Address string `json:"link_local_ipv6"` 104 GlobalIPv6PrefixLen int `json:"global_ipv6_prefix_len"` 105 IPv6Gateway string `json:"ipv6_gateway"` 106 } 107 108 type Resources struct { 109 Memory int64 `json:"memory"` 110 MemorySwap int64 `json:"memory_swap"` 111 CpuShares int64 `json:"cpu_shares"` 112 CpusetCpus string `json:"cpuset_cpus"` 113 CpusetMems string `json:"cpuset_mems"` 114 CpuQuota int64 `json:"cpu_quota"` 115 Rlimits []*ulimit.Rlimit `json:"rlimits"` 116 } 117 118 type ResourceStats struct { 119 *libcontainer.Stats 120 Read time.Time `json:"read"` 121 MemoryLimit int64 `json:"memory_limit"` 122 SystemUsage uint64 `json:"system_usage"` 123 } 124 125 type Mount struct { 126 Source string `json:"source"` 127 Destination string `json:"destination"` 128 Writable bool `json:"writable"` 129 Private bool `json:"private"` 130 Slave bool `json:"slave"` 131 } 132 133 // Describes a process that will be run inside a container. 134 type ProcessConfig struct { 135 exec.Cmd `json:"-"` 136 137 Privileged bool `json:"privileged"` 138 User string `json:"user"` 139 Tty bool `json:"tty"` 140 Entrypoint string `json:"entrypoint"` 141 Arguments []string `json:"arguments"` 142 Terminal Terminal `json:"-"` // standard or tty terminal 143 Console string `json:"-"` // dev/console path 144 } 145 146 // Process wrapps an os/exec.Cmd to add more metadata 147 type Command struct { 148 ID string `json:"id"` 149 Rootfs string `json:"rootfs"` // root fs of the container 150 ReadonlyRootfs bool `json:"readonly_rootfs"` 151 InitPath string `json:"initpath"` // dockerinit 152 WorkingDir string `json:"working_dir"` 153 ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver 154 Network *Network `json:"network"` 155 Ipc *Ipc `json:"ipc"` 156 Pid *Pid `json:"pid"` 157 Resources *Resources `json:"resources"` 158 Mounts []Mount `json:"mounts"` 159 AllowedDevices []*configs.Device `json:"allowed_devices"` 160 AutoCreatedDevices []*configs.Device `json:"autocreated_devices"` 161 CapAdd []string `json:"cap_add"` 162 CapDrop []string `json:"cap_drop"` 163 ContainerPid int `json:"container_pid"` // the pid for the process inside a container 164 ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container. 165 ProcessLabel string `json:"process_label"` 166 MountLabel string `json:"mount_label"` 167 LxcConfig []string `json:"lxc_config"` 168 AppArmorProfile string `json:"apparmor_profile"` 169 CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command. 170 } 171 172 func InitContainer(c *Command) *configs.Config { 173 container := template.New() 174 175 container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env) 176 container.Cgroups.Name = c.ID 177 container.Cgroups.AllowedDevices = c.AllowedDevices 178 container.Devices = c.AutoCreatedDevices 179 container.Rootfs = c.Rootfs 180 container.Readonlyfs = c.ReadonlyRootfs 181 182 // check to see if we are running in ramdisk to disable pivot root 183 container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" 184 185 // Default parent cgroup is "docker". Override if required. 186 if c.CgroupParent != "" { 187 container.Cgroups.Parent = c.CgroupParent 188 } 189 return container 190 } 191 192 func getEnv(key string, env []string) string { 193 for _, pair := range env { 194 parts := strings.Split(pair, "=") 195 if parts[0] == key { 196 return parts[1] 197 } 198 } 199 return "" 200 } 201 202 func SetupCgroups(container *configs.Config, c *Command) error { 203 if c.Resources != nil { 204 container.Cgroups.CpuShares = c.Resources.CpuShares 205 container.Cgroups.Memory = c.Resources.Memory 206 container.Cgroups.MemoryReservation = c.Resources.Memory 207 container.Cgroups.MemorySwap = c.Resources.MemorySwap 208 container.Cgroups.CpusetCpus = c.Resources.CpusetCpus 209 container.Cgroups.CpusetMems = c.Resources.CpusetMems 210 container.Cgroups.CpuQuota = c.Resources.CpuQuota 211 } 212 213 return nil 214 } 215 216 // Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. 217 func getNetworkInterfaceStats(interfaceName string) (*libcontainer.NetworkInterface, error) { 218 out := &libcontainer.NetworkInterface{Name: interfaceName} 219 // This can happen if the network runtime information is missing - possible if the 220 // container was created by an old version of libcontainer. 221 if interfaceName == "" { 222 return out, nil 223 } 224 type netStatsPair struct { 225 // Where to write the output. 226 Out *uint64 227 // The network stats file to read. 228 File string 229 } 230 // 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. 231 netStats := []netStatsPair{ 232 {Out: &out.RxBytes, File: "tx_bytes"}, 233 {Out: &out.RxPackets, File: "tx_packets"}, 234 {Out: &out.RxErrors, File: "tx_errors"}, 235 {Out: &out.RxDropped, File: "tx_dropped"}, 236 237 {Out: &out.TxBytes, File: "rx_bytes"}, 238 {Out: &out.TxPackets, File: "rx_packets"}, 239 {Out: &out.TxErrors, File: "rx_errors"}, 240 {Out: &out.TxDropped, File: "rx_dropped"}, 241 } 242 for _, netStat := range netStats { 243 data, err := readSysfsNetworkStats(interfaceName, netStat.File) 244 if err != nil { 245 return nil, err 246 } 247 *(netStat.Out) = data 248 } 249 return out, nil 250 } 251 252 // Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics 253 func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { 254 data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) 255 if err != nil { 256 return 0, err 257 } 258 return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) 259 } 260 261 func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) { 262 f, err := os.Open(filepath.Join(containerDir, "state.json")) 263 if err != nil { 264 return nil, err 265 } 266 defer f.Close() 267 268 type network struct { 269 Type string 270 HostInterfaceName string 271 } 272 273 state := struct { 274 CgroupPaths map[string]string `json:"cgroup_paths"` 275 Networks []network 276 }{} 277 278 if err := json.NewDecoder(f).Decode(&state); err != nil { 279 return nil, err 280 } 281 now := time.Now() 282 283 mgr := fs.Manager{Paths: state.CgroupPaths} 284 cstats, err := mgr.GetStats() 285 if err != nil { 286 return nil, err 287 } 288 stats := &libcontainer.Stats{CgroupStats: cstats} 289 // if the container does not have any memory limit specified set the 290 // limit to the machines memory 291 memoryLimit := containerMemoryLimit 292 if memoryLimit == 0 { 293 memoryLimit = machineMemory 294 } 295 for _, iface := range state.Networks { 296 switch iface.Type { 297 case "veth": 298 istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) 299 if err != nil { 300 return nil, err 301 } 302 stats.Interfaces = append(stats.Interfaces, istats) 303 } 304 } 305 return &ResourceStats{ 306 Stats: stats, 307 Read: now, 308 MemoryLimit: memoryLimit, 309 }, nil 310 }