mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge branch 'libcontainer-in-docker' into add-libcontainer
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
commit
89dbdb1f71
20 changed files with 811 additions and 238 deletions
|
@ -530,6 +530,7 @@ func (container *Container) Start() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
populateCommand(container)
|
populateCommand(container)
|
||||||
|
container.command.Env = env
|
||||||
|
|
||||||
// Setup logging of stdout and stderr to disk
|
// Setup logging of stdout and stderr to disk
|
||||||
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetTerminal(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
func SetTerminal(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
||||||
|
@ -26,8 +27,8 @@ func SetTerminal(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TtyConsole struct {
|
type TtyConsole struct {
|
||||||
master *os.File
|
MasterPty *os.File
|
||||||
slave *os.File
|
SlavePty *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
||||||
|
@ -36,28 +37,28 @@ func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyCo
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tty := &TtyConsole{
|
tty := &TtyConsole{
|
||||||
master: ptyMaster,
|
MasterPty: ptyMaster,
|
||||||
slave: ptySlave,
|
SlavePty: ptySlave,
|
||||||
}
|
}
|
||||||
if err := tty.attach(command, pipes); err != nil {
|
if err := tty.AttachPipes(&command.Cmd, pipes); err != nil {
|
||||||
tty.Close()
|
tty.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
command.Console = tty.SlavePty.Name()
|
||||||
return tty, nil
|
return tty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TtyConsole) Master() *os.File {
|
func (t *TtyConsole) Master() *os.File {
|
||||||
return t.master
|
return t.MasterPty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TtyConsole) Resize(h, w int) error {
|
func (t *TtyConsole) Resize(h, w int) error {
|
||||||
return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TtyConsole) attach(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||||
command.Stdout = t.slave
|
command.Stdout = t.SlavePty
|
||||||
command.Stderr = t.slave
|
command.Stderr = t.SlavePty
|
||||||
command.Console = t.slave.Name()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if wb, ok := pipes.Stdout.(interface {
|
if wb, ok := pipes.Stdout.(interface {
|
||||||
|
@ -65,24 +66,24 @@ func (t *TtyConsole) attach(command *execdriver.Command, pipes *execdriver.Pipes
|
||||||
}); ok {
|
}); ok {
|
||||||
defer wb.CloseWriters()
|
defer wb.CloseWriters()
|
||||||
}
|
}
|
||||||
io.Copy(pipes.Stdout, t.master)
|
io.Copy(pipes.Stdout, t.MasterPty)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if pipes.Stdin != nil {
|
if pipes.Stdin != nil {
|
||||||
command.Stdin = t.slave
|
command.Stdin = t.SlavePty
|
||||||
command.SysProcAttr.Setctty = true
|
command.SysProcAttr.Setctty = true
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer pipes.Stdin.Close()
|
defer pipes.Stdin.Close()
|
||||||
io.Copy(t.master, pipes.Stdin)
|
io.Copy(t.MasterPty, pipes.Stdin)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TtyConsole) Close() error {
|
func (t *TtyConsole) Close() error {
|
||||||
t.slave.Close()
|
t.SlavePty.Close()
|
||||||
return t.master.Close()
|
return t.MasterPty.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
type StdConsole struct {
|
type StdConsole struct {
|
||||||
|
@ -91,13 +92,13 @@ type StdConsole struct {
|
||||||
func NewStdConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*StdConsole, error) {
|
func NewStdConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*StdConsole, error) {
|
||||||
std := &StdConsole{}
|
std := &StdConsole{}
|
||||||
|
|
||||||
if err := std.attach(command, pipes); err != nil {
|
if err := std.AttachPipes(&command.Cmd, pipes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return std, nil
|
return std, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StdConsole) attach(command *execdriver.Command, pipes *execdriver.Pipes) error {
|
func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
||||||
command.Stdout = pipes.Stdout
|
command.Stdout = pipes.Stdout
|
||||||
command.Stderr = pipes.Stderr
|
command.Stderr = pipes.Stderr
|
||||||
|
|
||||||
|
|
41
execdriver/namespaces/default_template.go
Normal file
41
execdriver/namespaces/default_template.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dotcloud/docker/pkg/cgroups"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getDefaultTemplate returns the docker default for
|
||||||
|
// the libcontainer configuration file
|
||||||
|
func getDefaultTemplate() *libcontainer.Container {
|
||||||
|
return &libcontainer.Container{
|
||||||
|
Capabilities: libcontainer.Capabilities{
|
||||||
|
libcontainer.CAP_SETPCAP,
|
||||||
|
libcontainer.CAP_SYS_MODULE,
|
||||||
|
libcontainer.CAP_SYS_RAWIO,
|
||||||
|
libcontainer.CAP_SYS_PACCT,
|
||||||
|
libcontainer.CAP_SYS_ADMIN,
|
||||||
|
libcontainer.CAP_SYS_NICE,
|
||||||
|
libcontainer.CAP_SYS_RESOURCE,
|
||||||
|
libcontainer.CAP_SYS_TIME,
|
||||||
|
libcontainer.CAP_SYS_TTY_CONFIG,
|
||||||
|
libcontainer.CAP_MKNOD,
|
||||||
|
libcontainer.CAP_AUDIT_WRITE,
|
||||||
|
libcontainer.CAP_AUDIT_CONTROL,
|
||||||
|
libcontainer.CAP_MAC_ADMIN,
|
||||||
|
libcontainer.CAP_MAC_OVERRIDE,
|
||||||
|
libcontainer.CAP_NET_ADMIN,
|
||||||
|
},
|
||||||
|
Namespaces: libcontainer.Namespaces{
|
||||||
|
libcontainer.CLONE_NEWIPC,
|
||||||
|
libcontainer.CLONE_NEWNET,
|
||||||
|
libcontainer.CLONE_NEWNS,
|
||||||
|
libcontainer.CLONE_NEWPID,
|
||||||
|
libcontainer.CLONE_NEWUTS,
|
||||||
|
},
|
||||||
|
Cgroups: &cgroups.Cgroup{
|
||||||
|
Name: "docker",
|
||||||
|
DeviceAccess: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
216
execdriver/namespaces/driver.go
Normal file
216
execdriver/namespaces/driver.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/execdriver"
|
||||||
|
"github.com/dotcloud/docker/execdriver/lxc"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DriverName = "namespaces"
|
||||||
|
Version = "0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotSupported = errors.New("not supported")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type driver struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDriver() (*driver, error) {
|
||||||
|
return &driver{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||||
|
var (
|
||||||
|
term nsinit.Terminal
|
||||||
|
container = createContainer(c)
|
||||||
|
factory = &dockerCommandFactory{c}
|
||||||
|
stateWriter = &dockerStateWriter{
|
||||||
|
callback: startCallback,
|
||||||
|
c: c,
|
||||||
|
dsw: &nsinit.DefaultStateWriter{c.Rootfs},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if c.Tty {
|
||||||
|
term = &dockerTtyTerm{
|
||||||
|
pipes: pipes,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
term = &dockerStdTerm{
|
||||||
|
pipes: pipes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Terminal = term
|
||||||
|
if err := writeContainerFile(container, c.Rootfs); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
args := append([]string{c.Entrypoint}, c.Arguments...)
|
||||||
|
return nsinit.Exec(container, factory, stateWriter, term, "/nsinit.log", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
||||||
|
return p.Process.Kill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Restore(c *execdriver.Command) error {
|
||||||
|
return ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Info(id string) execdriver.Info {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Name() string {
|
||||||
|
return fmt.Sprintf("%s-%s", DriverName, Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeContainerFile(container *libcontainer.Container, rootfs string) error {
|
||||||
|
data, err := json.Marshal(container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(filepath.Join(rootfs, "container.json"), data, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnv(key string, env []string) string {
|
||||||
|
for _, pair := range env {
|
||||||
|
parts := strings.Split(pair, "=")
|
||||||
|
if parts[0] == key {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerCommandFactory struct {
|
||||||
|
c *execdriver.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||||
|
// defined on the container's configuration and use the current binary as the init with the
|
||||||
|
// args provided
|
||||||
|
func (d *dockerCommandFactory) Create(container *libcontainer.Container,
|
||||||
|
console, logFile string, syncFd uintptr, args []string) *exec.Cmd {
|
||||||
|
c := d.c
|
||||||
|
aname, _ := exec.LookPath("nsinit")
|
||||||
|
c.Path = aname
|
||||||
|
c.Args = append([]string{
|
||||||
|
aname,
|
||||||
|
"-console", console,
|
||||||
|
"-pipe", fmt.Sprint(syncFd),
|
||||||
|
"-log", logFile,
|
||||||
|
"init",
|
||||||
|
}, args...)
|
||||||
|
c.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Cloneflags: uintptr(nsinit.GetNamespaceFlags(container.Namespaces)),
|
||||||
|
}
|
||||||
|
c.Env = container.Env
|
||||||
|
c.Dir = c.Rootfs
|
||||||
|
|
||||||
|
return &c.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerStateWriter struct {
|
||||||
|
dsw nsinit.StateWriter
|
||||||
|
c *execdriver.Command
|
||||||
|
callback execdriver.StartCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerStateWriter) WritePid(pid int) error {
|
||||||
|
err := d.dsw.WritePid(pid)
|
||||||
|
if d.callback != nil {
|
||||||
|
d.callback(d.c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerStateWriter) DeletePid() error {
|
||||||
|
return d.dsw.DeletePid()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainer(c *execdriver.Command) *libcontainer.Container {
|
||||||
|
container := getDefaultTemplate()
|
||||||
|
|
||||||
|
container.Hostname = getEnv("HOSTNAME", c.Env)
|
||||||
|
container.Tty = c.Tty
|
||||||
|
container.User = c.User
|
||||||
|
container.WorkingDir = c.WorkingDir
|
||||||
|
container.Env = c.Env
|
||||||
|
|
||||||
|
container.Env = append(container.Env, "container=docker")
|
||||||
|
|
||||||
|
if c.Network != nil {
|
||||||
|
container.Network = &libcontainer.Network{
|
||||||
|
Mtu: c.Network.Mtu,
|
||||||
|
Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
|
||||||
|
Gateway: c.Network.Gateway,
|
||||||
|
Type: "veth",
|
||||||
|
Context: libcontainer.Context{
|
||||||
|
"prefix": "dock",
|
||||||
|
"bridge": c.Network.Bridge,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.Privileged {
|
||||||
|
container.Capabilities = nil
|
||||||
|
}
|
||||||
|
if c.Resources != nil {
|
||||||
|
container.Cgroups.CpuShares = c.Resources.CpuShares
|
||||||
|
container.Cgroups.Memory = c.Resources.Memory
|
||||||
|
container.Cgroups.MemorySwap = c.Resources.MemorySwap
|
||||||
|
}
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerStdTerm struct {
|
||||||
|
lxc.StdConsole
|
||||||
|
pipes *execdriver.Pipes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerStdTerm) Attach(cmd *exec.Cmd) error {
|
||||||
|
return d.AttachPipes(cmd, d.pipes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dockerStdTerm) SetMaster(master *os.File) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
type dockerTtyTerm struct {
|
||||||
|
lxc.TtyConsole
|
||||||
|
pipes *execdriver.Pipes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
|
||||||
|
go io.Copy(t.pipes.Stdout, t.MasterPty)
|
||||||
|
if t.pipes.Stdin != nil {
|
||||||
|
go io.Copy(t.MasterPty, t.pipes.Stdin)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dockerTtyTerm) SetMaster(master *os.File) {
|
||||||
|
t.MasterPty = master
|
||||||
|
}
|
26
execdriver/namespaces/term.go
Normal file
26
execdriver/namespaces/term.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dotcloud/docker/execdriver"
|
||||||
|
"github.com/dotcloud/docker/pkg/term"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NsinitTerm struct {
|
||||||
|
master *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTerm(pipes *execdriver.Pipes, master *os.File) *NsinitTerm {
|
||||||
|
return &NsinitTerm{master}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NsinitTerm) Close() error {
|
||||||
|
return t.master.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NsinitTerm) Resize(h, w int) error {
|
||||||
|
if t.master != nil {
|
||||||
|
return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -132,7 +132,7 @@ func (c *Cgroup) Apply(pid int) error {
|
||||||
// http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/
|
// http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/
|
||||||
//
|
//
|
||||||
// we can pick any subsystem to find the root
|
// we can pick any subsystem to find the root
|
||||||
cgroupRoot, err := FindCgroupMountpoint("memory")
|
cgroupRoot, err := FindCgroupMountpoint("cpu")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,17 @@ Sample `container.json` file:
|
||||||
"AUDIT_WRITE",
|
"AUDIT_WRITE",
|
||||||
"AUDIT_CONTROL",
|
"AUDIT_CONTROL",
|
||||||
"MAC_OVERRIDE",
|
"MAC_OVERRIDE",
|
||||||
"MAC_ADMIN"
|
"MAC_ADMIN",
|
||||||
|
"NET_ADMIN"
|
||||||
],
|
],
|
||||||
"network": {
|
"network": {
|
||||||
|
"type": "veth",
|
||||||
|
"context": {
|
||||||
|
"bridge": "docker0",
|
||||||
|
"prefix": "dock"
|
||||||
|
},
|
||||||
"address": "172.17.0.100/16",
|
"address": "172.17.0.100/16",
|
||||||
"gateway": "172.17.42.1",
|
"gateway": "172.17.42.1",
|
||||||
"bridge": "docker0",
|
|
||||||
"mtu": 1500
|
"mtu": 1500
|
||||||
},
|
},
|
||||||
"cgroups": {
|
"cgroups": {
|
||||||
|
|
|
@ -4,6 +4,10 @@ import (
|
||||||
"github.com/dotcloud/docker/pkg/cgroups"
|
"github.com/dotcloud/docker/pkg/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Context is a generic key value pair that allows
|
||||||
|
// arbatrary data to be sent
|
||||||
|
type Context map[string]string
|
||||||
|
|
||||||
// Container defines configuration options for how a
|
// Container defines configuration options for how a
|
||||||
// container is setup inside a directory and how a process should be executed
|
// container is setup inside a directory and how a process should be executed
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
@ -24,8 +28,9 @@ type Container struct {
|
||||||
// The network configuration can be omited from a container causing the
|
// The network configuration can be omited from a container causing the
|
||||||
// container to be setup with the host's networking stack
|
// container to be setup with the host's networking stack
|
||||||
type Network struct {
|
type Network struct {
|
||||||
|
Type string `json:"type,omitempty"` // type of networking to setup i.e. veth, macvlan, etc
|
||||||
|
Context Context `json:"context,omitempty"` // generic context for type specific networking options
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
Gateway string `json:"gateway,omitempty"`
|
Gateway string `json:"gateway,omitempty"`
|
||||||
Bridge string `json:"bridge,omitempty"`
|
|
||||||
Mtu int `json:"mtu,omitempty"`
|
Mtu int `json:"mtu,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,17 @@
|
||||||
"AUDIT_WRITE",
|
"AUDIT_WRITE",
|
||||||
"AUDIT_CONTROL",
|
"AUDIT_CONTROL",
|
||||||
"MAC_OVERRIDE",
|
"MAC_OVERRIDE",
|
||||||
"MAC_ADMIN"
|
"MAC_ADMIN",
|
||||||
|
"NET_ADMIN"
|
||||||
],
|
],
|
||||||
"network": {
|
"network": {
|
||||||
|
"type": "veth",
|
||||||
|
"context": {
|
||||||
|
"bridge": "docker0",
|
||||||
|
"prefix": "dock"
|
||||||
|
},
|
||||||
"address": "172.17.0.100/16",
|
"address": "172.17.0.100/16",
|
||||||
"gateway": "172.17.42.1",
|
"gateway": "172.17.42.1",
|
||||||
"bridge": "docker0",
|
|
||||||
"mtu": 1500
|
"mtu": 1500
|
||||||
},
|
},
|
||||||
"cgroups": {
|
"cgroups": {
|
||||||
|
|
32
pkg/libcontainer/network/strategy.go
Normal file
32
pkg/libcontainer/network/strategy.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotValidStrategyType = errors.New("not a valid network strategy type")
|
||||||
|
)
|
||||||
|
|
||||||
|
var strategies = map[string]NetworkStrategy{
|
||||||
|
"veth": &Veth{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkStrategy represends a specific network configuration for
|
||||||
|
// a containers networking stack
|
||||||
|
type NetworkStrategy interface {
|
||||||
|
Create(*libcontainer.Network, int) (libcontainer.Context, error)
|
||||||
|
Initialize(*libcontainer.Network, libcontainer.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStrategy returns the specific network strategy for the
|
||||||
|
// provided type. If no strategy is registered for the type an
|
||||||
|
// ErrNotValidStrategyType is returned.
|
||||||
|
func GetStrategy(tpe string) (NetworkStrategy, error) {
|
||||||
|
s, exists := strategies[tpe]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrNotValidStrategyType
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
103
pkg/libcontainer/network/veth.go
Normal file
103
pkg/libcontainer/network/veth.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Veth struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Veth) Create(n *libcontainer.Network, nspid int) (libcontainer.Context, error) {
|
||||||
|
log.Printf("creating veth network")
|
||||||
|
var (
|
||||||
|
bridge string
|
||||||
|
prefix string
|
||||||
|
exists bool
|
||||||
|
)
|
||||||
|
if bridge, exists = n.Context["bridge"]; !exists {
|
||||||
|
return nil, fmt.Errorf("bridge does not exist in network context")
|
||||||
|
}
|
||||||
|
if prefix, exists = n.Context["prefix"]; !exists {
|
||||||
|
return nil, fmt.Errorf("veth prefix does not exist in network context")
|
||||||
|
}
|
||||||
|
name1, name2, err := createVethPair(prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
context := libcontainer.Context{
|
||||||
|
"vethHost": name1,
|
||||||
|
"vethChild": name2,
|
||||||
|
}
|
||||||
|
log.Printf("veth pair created %s <> %s", name1, name2)
|
||||||
|
if err := SetInterfaceMaster(name1, bridge); err != nil {
|
||||||
|
return context, err
|
||||||
|
}
|
||||||
|
if err := SetMtu(name1, n.Mtu); err != nil {
|
||||||
|
return context, err
|
||||||
|
}
|
||||||
|
if err := InterfaceUp(name1); err != nil {
|
||||||
|
return context, err
|
||||||
|
}
|
||||||
|
log.Printf("setting %s inside %d namespace", name2, nspid)
|
||||||
|
if err := SetInterfaceInNamespacePid(name2, nspid); err != nil {
|
||||||
|
return context, err
|
||||||
|
}
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Context) error {
|
||||||
|
var (
|
||||||
|
vethChild string
|
||||||
|
exists bool
|
||||||
|
)
|
||||||
|
if vethChild, exists = context["vethChild"]; !exists {
|
||||||
|
return fmt.Errorf("vethChild does not exist in network context")
|
||||||
|
}
|
||||||
|
if err := InterfaceDown(vethChild); err != nil {
|
||||||
|
return fmt.Errorf("interface down %s %s", vethChild, err)
|
||||||
|
}
|
||||||
|
if err := ChangeInterfaceName(vethChild, "eth0"); err != nil {
|
||||||
|
return fmt.Errorf("change %s to eth0 %s", vethChild, err)
|
||||||
|
}
|
||||||
|
if err := SetInterfaceIp("eth0", config.Address); err != nil {
|
||||||
|
return fmt.Errorf("set eth0 ip %s", err)
|
||||||
|
}
|
||||||
|
if err := SetMtu("eth0", config.Mtu); err != nil {
|
||||||
|
return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
|
||||||
|
}
|
||||||
|
if err := InterfaceUp("eth0"); err != nil {
|
||||||
|
return fmt.Errorf("eth0 up %s", err)
|
||||||
|
}
|
||||||
|
if err := SetMtu("lo", config.Mtu); err != nil {
|
||||||
|
return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
|
||||||
|
}
|
||||||
|
if err := InterfaceUp("lo"); err != nil {
|
||||||
|
return fmt.Errorf("lo up %s", err)
|
||||||
|
}
|
||||||
|
if config.Gateway != "" {
|
||||||
|
if err := SetDefaultGateway(config.Gateway); err != nil {
|
||||||
|
return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createVethPair will automatically generage two random names for
|
||||||
|
// the veth pair and ensure that they have been created
|
||||||
|
func createVethPair(prefix string) (name1 string, name2 string, err error) {
|
||||||
|
name1, err = utils.GenerateRandomName(prefix, 4)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name2, err = utils.GenerateRandomName(prefix, 4)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = CreateVethPair(name1, name2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
34
pkg/libcontainer/nsinit/command.go
Normal file
34
pkg/libcontainer/nsinit/command.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package nsinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandFactory interface {
|
||||||
|
Create(container *libcontainer.Container, console, logFile string, syncFd uintptr, args []string) *exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultCommandFactory struct{}
|
||||||
|
|
||||||
|
// Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
||||||
|
// defined on the container's configuration and use the current binary as the init with the
|
||||||
|
// args provided
|
||||||
|
func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd {
|
||||||
|
// get our binary name so we can always reexec ourself
|
||||||
|
name := os.Args[0]
|
||||||
|
command := exec.Command(name, append([]string{
|
||||||
|
"-console", console,
|
||||||
|
"-pipe", fmt.Sprint(pipe),
|
||||||
|
"-log", logFile,
|
||||||
|
"init"}, args...)...)
|
||||||
|
|
||||||
|
command.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Cloneflags: uintptr(GetNamespaceFlags(container.Namespaces)),
|
||||||
|
}
|
||||||
|
command.Env = container.Env
|
||||||
|
return command
|
||||||
|
}
|
|
@ -3,14 +3,9 @@
|
||||||
package nsinit
|
package nsinit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer"
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/utils"
|
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
"github.com/dotcloud/docker/pkg/term"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -19,102 +14,65 @@ import (
|
||||||
|
|
||||||
// Exec performes setup outside of a namespace so that a container can be
|
// Exec performes setup outside of a namespace so that a container can be
|
||||||
// executed. Exec is a high level function for working with container namespaces.
|
// executed. Exec is a high level function for working with container namespaces.
|
||||||
func Exec(container *libcontainer.Container, logFile string, args []string) (int, error) {
|
func Exec(container *libcontainer.Container,
|
||||||
|
factory CommandFactory, state StateWriter, term Terminal,
|
||||||
|
logFile string, args []string) (int, error) {
|
||||||
var (
|
var (
|
||||||
master *os.File
|
master *os.File
|
||||||
console string
|
console string
|
||||||
err error
|
err error
|
||||||
|
|
||||||
inPipe io.WriteCloser
|
|
||||||
outPipe, errPipe io.ReadCloser
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if container.Tty {
|
|
||||||
log.Printf("setting up master and console")
|
|
||||||
master, console, err = createMasterAndConsole()
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a pipe so that we can syncronize with the namespaced process and
|
// create a pipe so that we can syncronize with the namespaced process and
|
||||||
// pass the veth name to the child
|
// pass the veth name to the child
|
||||||
r, w, err := os.Pipe()
|
syncPipe, err := NewSyncPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
system.UsetCloseOnExec(r.Fd())
|
|
||||||
|
|
||||||
command := createCommand(container, console, logFile, r.Fd(), args)
|
if container.Tty {
|
||||||
if !container.Tty {
|
log.Printf("setting up master and console")
|
||||||
log.Printf("opening pipes on command")
|
master, console, err = CreateMasterAndConsole()
|
||||||
if inPipe, err = command.StdinPipe(); err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
if outPipe, err = command.StdoutPipe(); err != nil {
|
term.SetMaster(master)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := factory.Create(container, console, logFile, syncPipe.child.Fd(), args)
|
||||||
|
if err := term.Attach(command); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
if errPipe, err = command.StderrPipe(); err != nil {
|
defer term.Close()
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("staring init")
|
log.Printf("staring init")
|
||||||
if err := command.Start(); err != nil {
|
if err := command.Start(); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
log.Printf("writting state file")
|
log.Printf("writing state file")
|
||||||
if err := writePidFile(command); err != nil {
|
if err := state.WritePid(command.Process.Pid); err != nil {
|
||||||
command.Process.Kill()
|
command.Process.Kill()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
defer deletePidFile()
|
defer func() {
|
||||||
|
log.Printf("removing state file")
|
||||||
|
state.DeletePid()
|
||||||
|
}()
|
||||||
|
|
||||||
// Do this before syncing with child so that no children
|
// Do this before syncing with child so that no children
|
||||||
// can escape the cgroup
|
// can escape the cgroup
|
||||||
if container.Cgroups != nil {
|
if err := SetupCgroups(container, command.Process.Pid); err != nil {
|
||||||
log.Printf("setting up cgroups")
|
|
||||||
if err := container.Cgroups.Apply(command.Process.Pid); err != nil {
|
|
||||||
command.Process.Kill()
|
command.Process.Kill()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
|
||||||
|
command.Process.Kill()
|
||||||
if container.Network != nil {
|
|
||||||
log.Printf("creating veth pair")
|
|
||||||
vethPair, err := initializeContainerVeth(container.Network.Bridge, container.Network.Mtu, command.Process.Pid)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
log.Printf("sending %s as veth pair name", vethPair)
|
|
||||||
sendVethName(w, vethPair)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync with child
|
// Sync with child
|
||||||
log.Printf("closing sync pipes")
|
log.Printf("closing sync pipes")
|
||||||
w.Close()
|
syncPipe.Close()
|
||||||
r.Close()
|
|
||||||
|
|
||||||
if container.Tty {
|
|
||||||
log.Printf("starting copy for tty")
|
|
||||||
go io.Copy(os.Stdout, master)
|
|
||||||
go io.Copy(master, os.Stdin)
|
|
||||||
|
|
||||||
state, err := setupWindow(master)
|
|
||||||
if err != nil {
|
|
||||||
command.Process.Kill()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
defer term.RestoreTerminal(os.Stdin.Fd(), state)
|
|
||||||
} else {
|
|
||||||
log.Printf("starting copy for std pipes")
|
|
||||||
go func() {
|
|
||||||
defer inPipe.Close()
|
|
||||||
io.Copy(inPipe, os.Stdin)
|
|
||||||
}()
|
|
||||||
go io.Copy(os.Stdout, outPipe)
|
|
||||||
go io.Copy(os.Stderr, errPipe)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("waiting on process")
|
log.Printf("waiting on process")
|
||||||
if err := command.Wait(); err != nil {
|
if err := command.Wait(); err != nil {
|
||||||
|
@ -126,55 +84,38 @@ func Exec(container *libcontainer.Container, logFile string, args []string) (int
|
||||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendVethName writes the veth pair name to the child's stdin then closes the
|
func SetupCgroups(container *libcontainer.Container, nspid int) error {
|
||||||
// pipe so that the child stops waiting for more data
|
if container.Cgroups != nil {
|
||||||
func sendVethName(pipe io.Writer, name string) {
|
log.Printf("setting up cgroups")
|
||||||
fmt.Fprint(pipe, name)
|
if err := container.Cgroups.Apply(nspid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeContainerVeth will create a veth pair and setup the host's
|
func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
|
||||||
// side of the pair by setting the specified bridge as the master and bringing
|
if container.Network != nil {
|
||||||
// up the interface.
|
log.Printf("creating host network configuration type %s", container.Network.Type)
|
||||||
//
|
strategy, err := network.GetStrategy(container.Network.Type)
|
||||||
// Then will with set the other side of the veth pair into the container's namespaced
|
|
||||||
// using the pid and returns the veth's interface name to provide to the container to
|
|
||||||
// finish setting up the interface inside the namespace
|
|
||||||
func initializeContainerVeth(bridge string, mtu, nspid int) (string, error) {
|
|
||||||
name1, name2, err := createVethPair()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("veth pair created %s <> %s", name1, name2)
|
networkContext, err := strategy.Create(container.Network, nspid)
|
||||||
if err := network.SetInterfaceMaster(name1, bridge); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := network.SetMtu(name1, mtu); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := network.InterfaceUp(name1); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
log.Printf("setting %s inside %d namespace", name2, nspid)
|
|
||||||
if err := network.SetInterfaceInNamespacePid(name2, nspid); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return name2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupWindow(master *os.File) (*term.State, error) {
|
|
||||||
ws, err := term.GetWinsize(os.Stdin.Fd())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if err := term.SetWinsize(master.Fd(), ws); err != nil {
|
log.Printf("sending %v as network context", networkContext)
|
||||||
return nil, err
|
if err := pipe.SendToChild(networkContext); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return term.SetRawTerminal(os.Stdin.Fd())
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMasterAndConsole will open /dev/ptmx on the host and retreive the
|
// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
|
||||||
// pts name for use as the pty slave inside the container
|
// pts name for use as the pty slave inside the container
|
||||||
func createMasterAndConsole() (*os.File, string, error) {
|
func CreateMasterAndConsole() (*os.File, string, error) {
|
||||||
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
@ -188,45 +129,3 @@ func createMasterAndConsole() (*os.File, string, error) {
|
||||||
}
|
}
|
||||||
return master, console, nil
|
return master, console, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createVethPair will automatically generage two random names for
|
|
||||||
// the veth pair and ensure that they have been created
|
|
||||||
func createVethPair() (name1 string, name2 string, err error) {
|
|
||||||
name1, err = utils.GenerateRandomName("dock", 4)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name2, err = utils.GenerateRandomName("dock", 4)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = network.CreateVethPair(name1, name2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// writePidFile writes the namespaced processes pid to .nspid in the rootfs for the container
|
|
||||||
func writePidFile(command *exec.Cmd) error {
|
|
||||||
return ioutil.WriteFile(".nspid", []byte(fmt.Sprint(command.Process.Pid)), 0655)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deletePidFile() error {
|
|
||||||
return os.Remove(".nspid")
|
|
||||||
}
|
|
||||||
|
|
||||||
// createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
|
|
||||||
// defined on the container's configuration and use the current binary as the init with the
|
|
||||||
// args provided
|
|
||||||
func createCommand(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd {
|
|
||||||
command := exec.Command("nsinit", append([]string{
|
|
||||||
"-console", console,
|
|
||||||
"-pipe", fmt.Sprint(pipe),
|
|
||||||
"-log", logFile,
|
|
||||||
"init"}, args...)...)
|
|
||||||
|
|
||||||
command.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Cloneflags: uintptr(getNamespaceFlags(container.Namespaces)),
|
|
||||||
}
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,17 +8,16 @@ import (
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
|
"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
|
||||||
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
"github.com/dotcloud/docker/pkg/libcontainer/network"
|
||||||
"github.com/dotcloud/docker/pkg/system"
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
|
||||||
// and other options required for the new container.
|
// and other options required for the new container.
|
||||||
func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe io.ReadCloser, args []string) error {
|
func Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
|
||||||
rootfs, err := resolveRootfs(uncleanRootfs)
|
rootfs, err := resolveRootfs(uncleanRootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -26,19 +25,18 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
|
||||||
log.Printf("initializing namespace at %s", rootfs)
|
log.Printf("initializing namespace at %s", rootfs)
|
||||||
|
|
||||||
// We always read this as it is a way to sync with the parent as well
|
// We always read this as it is a way to sync with the parent as well
|
||||||
tempVethName, err := getVethName(pipe)
|
context, err := syncPipe.ReadFromParent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
syncPipe.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tempVethName != "" {
|
syncPipe.Close()
|
||||||
log.Printf("received veth name %s", tempVethName)
|
log.Printf("received context from parent %v", context)
|
||||||
}
|
|
||||||
if console != "" {
|
if console != "" {
|
||||||
log.Printf("setting up console for %s", console)
|
log.Printf("setting up console for %s", console)
|
||||||
// close pipes so that we can replace it with the pty
|
// close pipes so that we can replace it with the pty
|
||||||
os.Stdin.Close()
|
closeStdPipes()
|
||||||
os.Stdout.Close()
|
|
||||||
os.Stderr.Close()
|
|
||||||
slave, err := openTerminal(console, syscall.O_RDWR)
|
slave, err := openTerminal(console, syscall.O_RDWR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open terminal %s", err)
|
return fmt.Errorf("open terminal %s", err)
|
||||||
|
@ -61,7 +59,7 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
|
||||||
if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
|
if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs); err != nil {
|
||||||
return fmt.Errorf("setup mount namespace %s", err)
|
return fmt.Errorf("setup mount namespace %s", err)
|
||||||
}
|
}
|
||||||
if err := setupVethNetwork(container.Network, tempVethName); err != nil {
|
if err := setupNetwork(container.Network, context); err != nil {
|
||||||
return fmt.Errorf("setup networking %s", err)
|
return fmt.Errorf("setup networking %s", err)
|
||||||
}
|
}
|
||||||
if err := system.Sethostname(container.Hostname); err != nil {
|
if err := system.Sethostname(container.Hostname); err != nil {
|
||||||
|
@ -80,13 +78,27 @@ func Init(container *libcontainer.Container, uncleanRootfs, console string, pipe
|
||||||
return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
|
return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("execing %s goodbye", args[0])
|
return execArgs(args, container.Env)
|
||||||
if err := system.Exec(args[0], args[0:], container.Env); err != nil {
|
}
|
||||||
|
|
||||||
|
func execArgs(args []string, env []string) error {
|
||||||
|
name, err := exec.LookPath(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("execing %s goodbye", name)
|
||||||
|
if err := system.Exec(name, args[0:], env); err != nil {
|
||||||
return fmt.Errorf("exec %s", err)
|
return fmt.Errorf("exec %s", err)
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func closeStdPipes() {
|
||||||
|
os.Stdin.Close()
|
||||||
|
os.Stdout.Close()
|
||||||
|
os.Stderr.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// resolveRootfs ensures that the current working directory is
|
// resolveRootfs ensures that the current working directory is
|
||||||
// not a symlink and returns the absolute path to the rootfs
|
// not a symlink and returns the absolute path to the rootfs
|
||||||
func resolveRootfs(uncleanRootfs string) (string, error) {
|
func resolveRootfs(uncleanRootfs string) (string, error) {
|
||||||
|
@ -139,46 +151,13 @@ func openTerminal(name string, flag int) (*os.File, error) {
|
||||||
// setupVethNetwork uses the Network config if it is not nil to initialize
|
// setupVethNetwork uses the Network config if it is not nil to initialize
|
||||||
// the new veth interface inside the container for use by changing the name to eth0
|
// the new veth interface inside the container for use by changing the name to eth0
|
||||||
// setting the MTU and IP address along with the default gateway
|
// setting the MTU and IP address along with the default gateway
|
||||||
func setupVethNetwork(config *libcontainer.Network, tempVethName string) error {
|
func setupNetwork(config *libcontainer.Network, context libcontainer.Context) error {
|
||||||
if config != nil {
|
if config != nil {
|
||||||
if err := network.InterfaceDown(tempVethName); err != nil {
|
strategy, err := network.GetStrategy(config.Type)
|
||||||
return fmt.Errorf("interface down %s %s", tempVethName, err)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
if err := network.ChangeInterfaceName(tempVethName, "eth0"); err != nil {
|
|
||||||
return fmt.Errorf("change %s to eth0 %s", tempVethName, err)
|
|
||||||
}
|
|
||||||
if err := network.SetInterfaceIp("eth0", config.Address); err != nil {
|
|
||||||
return fmt.Errorf("set eth0 ip %s", err)
|
|
||||||
}
|
|
||||||
if err := network.SetMtu("eth0", config.Mtu); err != nil {
|
|
||||||
return fmt.Errorf("set eth0 mtu to %d %s", config.Mtu, err)
|
|
||||||
}
|
|
||||||
if err := network.InterfaceUp("eth0"); err != nil {
|
|
||||||
return fmt.Errorf("eth0 up %s", err)
|
|
||||||
}
|
|
||||||
if err := network.SetMtu("lo", config.Mtu); err != nil {
|
|
||||||
return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
|
|
||||||
}
|
|
||||||
if err := network.InterfaceUp("lo"); err != nil {
|
|
||||||
return fmt.Errorf("lo up %s", err)
|
|
||||||
}
|
|
||||||
if config.Gateway != "" {
|
|
||||||
if err := network.SetDefaultGateway(config.Gateway); err != nil {
|
|
||||||
return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return strategy.Initialize(config, context)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVethName reads from Stdin the temp veth name
|
|
||||||
// sent by the parent processes after the veth pair
|
|
||||||
// has been created and setup
|
|
||||||
func getVethName(pipe io.ReadCloser) (string, error) {
|
|
||||||
defer pipe.Close()
|
|
||||||
data, err := ioutil.ReadAll(pipe)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("error reading from stdin %s", err)
|
|
||||||
}
|
|
||||||
return string(data), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ var namespaceFileMap = map[libcontainer.Namespace]string{
|
||||||
|
|
||||||
// getNamespaceFlags parses the container's Namespaces options to set the correct
|
// getNamespaceFlags parses the container's Namespaces options to set the correct
|
||||||
// flags on clone, unshare, and setns
|
// flags on clone, unshare, and setns
|
||||||
func getNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
flag |= namespaceMap[ns]
|
flag |= namespaceMap[ns]
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,11 @@ func main() {
|
||||||
if nspid > 0 {
|
if nspid > 0 {
|
||||||
exitCode, err = nsinit.ExecIn(container, nspid, flag.Args()[1:])
|
exitCode, err = nsinit.ExecIn(container, nspid, flag.Args()[1:])
|
||||||
} else {
|
} else {
|
||||||
exitCode, err = nsinit.Exec(container, logFile, flag.Args()[1:])
|
term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
|
||||||
|
exitCode, err = nsinit.Exec(container,
|
||||||
|
&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{},
|
||||||
|
term,
|
||||||
|
logFile, flag.Args()[1:])
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -72,7 +76,11 @@ func main() {
|
||||||
if flag.NArg() < 2 {
|
if flag.NArg() < 2 {
|
||||||
log.Fatal(ErrWrongArguments)
|
log.Fatal(ErrWrongArguments)
|
||||||
}
|
}
|
||||||
if err := nsinit.Init(container, cwd, console, os.NewFile(uintptr(pipeFd), "pipe"), flag.Args()[1:]); err != nil {
|
syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := nsinit.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
26
pkg/libcontainer/nsinit/state.go
Normal file
26
pkg/libcontainer/nsinit/state.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package nsinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StateWriter interface {
|
||||||
|
WritePid(pid int) error
|
||||||
|
DeletePid() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultStateWriter struct {
|
||||||
|
Root string
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePidFile writes the namespaced processes pid to .nspid in the rootfs for the container
|
||||||
|
func (d *DefaultStateWriter) WritePid(pid int) error {
|
||||||
|
return ioutil.WriteFile(filepath.Join(d.Root, ".nspid"), []byte(fmt.Sprint(pid)), 0655)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultStateWriter) DeletePid() error {
|
||||||
|
return os.Remove(filepath.Join(d.Root, ".nspid"))
|
||||||
|
}
|
73
pkg/libcontainer/nsinit/sync_pipe.go
Normal file
73
pkg/libcontainer/nsinit/sync_pipe.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package nsinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/pkg/libcontainer"
|
||||||
|
"github.com/dotcloud/docker/pkg/system"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SyncPipe allows communication to and from the child processes
|
||||||
|
// to it's parent and allows the two independent processes to
|
||||||
|
// syncronize their state.
|
||||||
|
type SyncPipe struct {
|
||||||
|
parent, child *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncPipe() (s *SyncPipe, err error) {
|
||||||
|
s = &SyncPipe{}
|
||||||
|
s.child, s.parent, err = os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
system.UsetCloseOnExec(s.child.Fd())
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncPipeFromFd(parendFd, childFd uintptr) (*SyncPipe, error) {
|
||||||
|
s := &SyncPipe{}
|
||||||
|
if parendFd > 0 {
|
||||||
|
s.parent = os.NewFile(parendFd, "parendPipe")
|
||||||
|
} else if childFd > 0 {
|
||||||
|
s.child = os.NewFile(childFd, "childPipe")
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("no valid sync pipe fd specified")
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) SendToChild(context libcontainer.Context) error {
|
||||||
|
data, err := json.Marshal(context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.parent.Write(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) ReadFromParent() (libcontainer.Context, error) {
|
||||||
|
data, err := ioutil.ReadAll(s.child)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading from sync pipe %s", err)
|
||||||
|
}
|
||||||
|
var context libcontainer.Context
|
||||||
|
if len(data) > 0 {
|
||||||
|
if err := json.Unmarshal(data, &context); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SyncPipe) Close() error {
|
||||||
|
if s.parent != nil {
|
||||||
|
s.parent.Close()
|
||||||
|
}
|
||||||
|
if s.child != nil {
|
||||||
|
s.child.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
118
pkg/libcontainer/nsinit/term.go
Normal file
118
pkg/libcontainer/nsinit/term.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package nsinit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dotcloud/docker/pkg/term"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Terminal interface {
|
||||||
|
io.Closer
|
||||||
|
SetMaster(*os.File)
|
||||||
|
Attach(*exec.Cmd) error
|
||||||
|
Resize(h, w int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTerminal(stdin io.Reader, stdout, stderr io.Writer, tty bool) Terminal {
|
||||||
|
if tty {
|
||||||
|
return &TtyTerminal{
|
||||||
|
stdin: stdin,
|
||||||
|
stdout: stdout,
|
||||||
|
stderr: stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &StdTerminal{
|
||||||
|
stdin: stdin,
|
||||||
|
stdout: stdout,
|
||||||
|
stderr: stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TtyTerminal struct {
|
||||||
|
stdin io.Reader
|
||||||
|
stdout, stderr io.Writer
|
||||||
|
master *os.File
|
||||||
|
state *term.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TtyTerminal) Resize(h, w int) error {
|
||||||
|
return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TtyTerminal) SetMaster(master *os.File) {
|
||||||
|
t.master = master
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TtyTerminal) Attach(command *exec.Cmd) error {
|
||||||
|
go io.Copy(t.stdout, t.master)
|
||||||
|
go io.Copy(t.master, t.stdin)
|
||||||
|
|
||||||
|
state, err := t.setupWindow(t.master, os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
command.Process.Kill()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.state = state
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWindow gets the parent window size and sets the master
|
||||||
|
// pty to the current size and set the parents mode to RAW
|
||||||
|
func (t *TtyTerminal) setupWindow(master, parent *os.File) (*term.State, error) {
|
||||||
|
ws, err := term.GetWinsize(parent.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := term.SetWinsize(master.Fd(), ws); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return term.SetRawTerminal(parent.Fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TtyTerminal) Close() error {
|
||||||
|
term.RestoreTerminal(os.Stdin.Fd(), t.state)
|
||||||
|
return t.master.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type StdTerminal struct {
|
||||||
|
stdin io.Reader
|
||||||
|
stdout, stderr io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdTerminal) SetMaster(*os.File) {
|
||||||
|
// no need to set master on non tty
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdTerminal) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdTerminal) Resize(h, w int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StdTerminal) Attach(command *exec.Cmd) error {
|
||||||
|
inPipe, err := command.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
outPipe, err := command.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
errPipe, err := command.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer inPipe.Close()
|
||||||
|
io.Copy(inPipe, s.stdin)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go io.Copy(s.stdout, outPipe)
|
||||||
|
go io.Copy(s.stderr, errPipe)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ import (
|
||||||
"github.com/dotcloud/docker/dockerversion"
|
"github.com/dotcloud/docker/dockerversion"
|
||||||
"github.com/dotcloud/docker/engine"
|
"github.com/dotcloud/docker/engine"
|
||||||
"github.com/dotcloud/docker/execdriver"
|
"github.com/dotcloud/docker/execdriver"
|
||||||
"github.com/dotcloud/docker/execdriver/lxc"
|
_ "github.com/dotcloud/docker/execdriver/lxc"
|
||||||
|
"github.com/dotcloud/docker/execdriver/namespaces"
|
||||||
"github.com/dotcloud/docker/graphdriver"
|
"github.com/dotcloud/docker/graphdriver"
|
||||||
"github.com/dotcloud/docker/graphdriver/aufs"
|
"github.com/dotcloud/docker/graphdriver/aufs"
|
||||||
_ "github.com/dotcloud/docker/graphdriver/btrfs"
|
_ "github.com/dotcloud/docker/graphdriver/btrfs"
|
||||||
|
@ -703,7 +704,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig, eng *engine.Engine) (*Runtime
|
||||||
|
|
||||||
sysInfo := sysinfo.New(false)
|
sysInfo := sysinfo.New(false)
|
||||||
|
|
||||||
ed, err := lxc.NewDriver(config.Root, sysInfo.AppArmor)
|
ed, err := namespaces.NewDriver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue