github.com/scorpionis/docker@v1.6.0-rc7/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 Rlimits []*ulimit.Rlimit `json:"rlimits"` 114 } 115 116 type ResourceStats struct { 117 *libcontainer.Stats 118 Read time.Time `json:"read"` 119 MemoryLimit int64 `json:"memory_limit"` 120 SystemUsage uint64 `json:"system_usage"` 121 } 122 123 type Mount struct { 124 Source string `json:"source"` 125 Destination string `json:"destination"` 126 Writable bool `json:"writable"` 127 Private bool `json:"private"` 128 Slave bool `json:"slave"` 129 } 130 131 // Describes a process that will be run inside a container. 132 type ProcessConfig struct { 133 exec.Cmd `json:"-"` 134 135 Privileged bool `json:"privileged"` 136 User string `json:"user"` 137 Tty bool `json:"tty"` 138 Entrypoint string `json:"entrypoint"` 139 Arguments []string `json:"arguments"` 140 Terminal Terminal `json:"-"` // standard or tty terminal 141 Console string `json:"-"` // dev/console path 142 } 143 144 // Process wrapps an os/exec.Cmd to add more metadata 145 type Command struct { 146 ID string `json:"id"` 147 Rootfs string `json:"rootfs"` // root fs of the container 148 ReadonlyRootfs bool `json:"readonly_rootfs"` 149 InitPath string `json:"initpath"` // dockerinit 150 WorkingDir string `json:"working_dir"` 151 ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver 152 Network *Network `json:"network"` 153 Ipc *Ipc `json:"ipc"` 154 Pid *Pid `json:"pid"` 155 Resources *Resources `json:"resources"` 156 Mounts []Mount `json:"mounts"` 157 AllowedDevices []*configs.Device `json:"allowed_devices"` 158 AutoCreatedDevices []*configs.Device `json:"autocreated_devices"` 159 CapAdd []string `json:"cap_add"` 160 CapDrop []string `json:"cap_drop"` 161 ContainerPid int `json:"container_pid"` // the pid for the process inside a container 162 ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container. 163 ProcessLabel string `json:"process_label"` 164 MountLabel string `json:"mount_label"` 165 LxcConfig []string `json:"lxc_config"` 166 AppArmorProfile string `json:"apparmor_profile"` 167 CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command. 168 } 169 170 func InitContainer(c *Command) *configs.Config { 171 container := template.New() 172 173 container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env) 174 container.Cgroups.Name = c.ID 175 container.Cgroups.AllowedDevices = c.AllowedDevices 176 container.Readonlyfs = c.ReadonlyRootfs 177 container.Devices = c.AutoCreatedDevices 178 container.Rootfs = c.Rootfs 179 container.Readonlyfs = c.ReadonlyRootfs 180 181 // check to see if we are running in ramdisk to disable pivot root 182 container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" 183 184 // Default parent cgroup is "docker". Override if required. 185 if c.CgroupParent != "" { 186 container.Cgroups.Parent = c.CgroupParent 187 } 188 return container 189 } 190 191 func getEnv(key string, env []string) string { 192 for _, pair := range env { 193 parts := strings.Split(pair, "=") 194 if parts[0] == key { 195 return parts[1] 196 } 197 } 198 return "" 199 } 200 201 func SetupCgroups(container *configs.Config, c *Command) error { 202 if c.Resources != nil { 203 container.Cgroups.CpuShares = c.Resources.CpuShares 204 container.Cgroups.Memory = c.Resources.Memory 205 container.Cgroups.MemoryReservation = c.Resources.Memory 206 container.Cgroups.MemorySwap = c.Resources.MemorySwap 207 container.Cgroups.CpusetCpus = c.Resources.CpusetCpus 208 } 209 210 return nil 211 } 212 213 // Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. 214 func getNetworkInterfaceStats(interfaceName string) (*libcontainer.NetworkInterface, error) { 215 out := &libcontainer.NetworkInterface{Name: interfaceName} 216 // This can happen if the network runtime information is missing - possible if the 217 // container was created by an old version of libcontainer. 218 if interfaceName == "" { 219 return out, nil 220 } 221 type netStatsPair struct { 222 // Where to write the output. 223 Out *uint64 224 // The network stats file to read. 225 File string 226 } 227 // 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. 228 netStats := []netStatsPair{ 229 {Out: &out.RxBytes, File: "tx_bytes"}, 230 {Out: &out.RxPackets, File: "tx_packets"}, 231 {Out: &out.RxErrors, File: "tx_errors"}, 232 {Out: &out.RxDropped, File: "tx_dropped"}, 233 234 {Out: &out.TxBytes, File: "rx_bytes"}, 235 {Out: &out.TxPackets, File: "rx_packets"}, 236 {Out: &out.TxErrors, File: "rx_errors"}, 237 {Out: &out.TxDropped, File: "rx_dropped"}, 238 } 239 for _, netStat := range netStats { 240 data, err := readSysfsNetworkStats(interfaceName, netStat.File) 241 if err != nil { 242 return nil, err 243 } 244 *(netStat.Out) = data 245 } 246 return out, nil 247 } 248 249 // Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics 250 func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { 251 data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) 252 if err != nil { 253 return 0, err 254 } 255 return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) 256 } 257 258 func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) { 259 f, err := os.Open(filepath.Join(containerDir, "state.json")) 260 if err != nil { 261 return nil, err 262 } 263 defer f.Close() 264 265 type network struct { 266 Type string 267 HostInterfaceName string 268 } 269 270 state := struct { 271 CgroupPaths map[string]string `json:"cgroup_paths"` 272 Networks []network 273 }{} 274 275 if err := json.NewDecoder(f).Decode(&state); err != nil { 276 return nil, err 277 } 278 now := time.Now() 279 280 mgr := fs.Manager{Paths: state.CgroupPaths} 281 cstats, err := mgr.GetStats() 282 if err != nil { 283 return nil, err 284 } 285 stats := &libcontainer.Stats{CgroupStats: cstats} 286 // if the container does not have any memory limit specified set the 287 // limit to the machines memory 288 memoryLimit := containerMemoryLimit 289 if memoryLimit == 0 { 290 memoryLimit = machineMemory 291 } 292 for _, iface := range state.Networks { 293 switch iface.Type { 294 case "veth": 295 istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) 296 if err != nil { 297 return nil, err 298 } 299 stats.Interfaces = append(stats.Interfaces, istats) 300 } 301 } 302 return &ResourceStats{ 303 Stats: stats, 304 Read: now, 305 MemoryLimit: memoryLimit, 306 }, nil 307 }