|
@@ -1,21 +1,14 @@
|
|
package execdriver
|
|
package execdriver
|
|
|
|
|
|
import (
|
|
import (
|
|
- "encoding/json"
|
|
|
|
"errors"
|
|
"errors"
|
|
"io"
|
|
"io"
|
|
- "io/ioutil"
|
|
|
|
- "os"
|
|
|
|
"os/exec"
|
|
"os/exec"
|
|
- "path/filepath"
|
|
|
|
- "strconv"
|
|
|
|
- "strings"
|
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
- "github.com/docker/docker/daemon/execdriver/native/template"
|
|
|
|
|
|
+ // TODO Windows: Factor out ulimit
|
|
"github.com/docker/docker/pkg/ulimit"
|
|
"github.com/docker/docker/pkg/ulimit"
|
|
"github.com/docker/libcontainer"
|
|
"github.com/docker/libcontainer"
|
|
- "github.com/docker/libcontainer/cgroups/fs"
|
|
|
|
"github.com/docker/libcontainer/configs"
|
|
"github.com/docker/libcontainer/configs"
|
|
)
|
|
)
|
|
|
|
|
|
@@ -105,6 +98,7 @@ type NetworkInterface struct {
|
|
IPv6Gateway string `json:"ipv6_gateway"`
|
|
IPv6Gateway string `json:"ipv6_gateway"`
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// TODO Windows: Factor out ulimit.Rlimit
|
|
type Resources struct {
|
|
type Resources struct {
|
|
Memory int64 `json:"memory"`
|
|
Memory int64 `json:"memory"`
|
|
MemorySwap int64 `json:"memory_swap"`
|
|
MemorySwap int64 `json:"memory_swap"`
|
|
@@ -143,6 +137,9 @@ type ProcessConfig struct {
|
|
Console string `json:"-"` // dev/console path
|
|
Console string `json:"-"` // dev/console path
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// TODO Windows: Factor out unused fields such as LxcConfig, AppArmorProfile,
|
|
|
|
+// and CgroupParent.
|
|
|
|
+//
|
|
// Process wrapps an os/exec.Cmd to add more metadata
|
|
// Process wrapps an os/exec.Cmd to add more metadata
|
|
type Command struct {
|
|
type Command struct {
|
|
ID string `json:"id"`
|
|
ID string `json:"id"`
|
|
@@ -168,143 +165,3 @@ type Command struct {
|
|
AppArmorProfile string `json:"apparmor_profile"`
|
|
AppArmorProfile string `json:"apparmor_profile"`
|
|
CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command.
|
|
CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command.
|
|
}
|
|
}
|
|
-
|
|
|
|
-func InitContainer(c *Command) *configs.Config {
|
|
|
|
- container := template.New()
|
|
|
|
-
|
|
|
|
- container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env)
|
|
|
|
- container.Cgroups.Name = c.ID
|
|
|
|
- container.Cgroups.AllowedDevices = c.AllowedDevices
|
|
|
|
- container.Devices = c.AutoCreatedDevices
|
|
|
|
- container.Rootfs = c.Rootfs
|
|
|
|
- container.Readonlyfs = c.ReadonlyRootfs
|
|
|
|
-
|
|
|
|
- // check to see if we are running in ramdisk to disable pivot root
|
|
|
|
- container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
|
|
|
-
|
|
|
|
- // Default parent cgroup is "docker". Override if required.
|
|
|
|
- if c.CgroupParent != "" {
|
|
|
|
- container.Cgroups.Parent = c.CgroupParent
|
|
|
|
- }
|
|
|
|
- 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 ""
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func SetupCgroups(container *configs.Config, c *Command) error {
|
|
|
|
- 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.CpusetCpus
|
|
|
|
- container.Cgroups.CpusetMems = c.Resources.CpusetMems
|
|
|
|
- container.Cgroups.CpuQuota = c.Resources.CpuQuota
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// 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
|
|
|
|
- }
|
|
|
|
- *(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 {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- now := time.Now()
|
|
|
|
-
|
|
|
|
- mgr := fs.Manager{Paths: state.CgroupPaths}
|
|
|
|
- cstats, err := mgr.GetStats()
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- stats := &libcontainer.Stats{CgroupStats: cstats}
|
|
|
|
- // if the container does not have any memory limit specified set the
|
|
|
|
- // limit to the machines memory
|
|
|
|
- memoryLimit := containerMemoryLimit
|
|
|
|
- if memoryLimit == 0 {
|
|
|
|
- memoryLimit = machineMemory
|
|
|
|
- }
|
|
|
|
- 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)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return &ResourceStats{
|
|
|
|
- Stats: stats,
|
|
|
|
- Read: now,
|
|
|
|
- MemoryLimit: memoryLimit,
|
|
|
|
- }, nil
|
|
|
|
-}
|
|
|