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  }